scala使用Gson解析数组

2 篇文章 0 订阅

完整代码在最后哦,可以通过目录跳转

一、背景

假设某个HTTP接口的返回值如下,怎么在scala中使用Gsonresult解析出来呢?

{
    "code":0,
    "message":"OK",
    "result":[
        {
            "originalityId":7,
            "conversionType":10011,
            "conversionTypeValue":"打点激活",
            "targetValueDouble":"1.0"
        }
    ]
}

二、解决方案

1. 高版本的解法(2.8.7)

本来的解法是这样的:

    private def parseBody(bodyOpt: Option[JsonObject]) = {
        val isLegal = bodyOpt.isDefined && {
            val body = bodyOpt.get
            body.has(CODE) && body.get(CODE).getAsInt == SUCCESS_CODE && body.has(RESULT)
        }

        if (!isLegal) throw new RuntimeException(s"bodyOpt is illegal, bodyOpt: $bodyOpt")

        val body = bodyOpt.get
        val results = body.get(RESULT).getAsJsonArray

        val resultList = new ArrayBuffer[(Long, (Long, Int, String, Double))]()
        results.forEach(r => {
            val result = r.getAsJsonObject
            val adId = result.get(ORIGINALITY_ID).getAsLong
            val conversionType = result.get(CONVERSION_TYPE).getAsInt
            val conversionTypeValue = result.get(CONVERSION_TYPE_VALUE).getAsString
            val targetValueDouble = result.get(TARGET_VALUE_DOUBLE).getAsDouble
            resultList.append(adId -> (adId, conversionType, conversionTypeValue, targetValueDouble))
        })

        resultList.toList
    }

这个示例用的Gson版本为2.8.7,依赖如下:

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.7</version>
        </dependency>
2. 低版本的解法(2.8.2)

但是上面的解法貌似只在高版本Gson中支持,但是2.8.2的版本好像就不行了:

在这里插入图片描述

在这里插入图片描述

foreach方法,其实是需要接收一个Consumer类型的算子:

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

Java里,传一个lambda表达式就好了,但是在scala里,貌似不支持这种操作,那怎么办呢,自己实现一个Consumer对象不就好了,于是写出了下面这样的scala代码:

        val list = new ArrayBuffer[JsonObject]()

        val consumer = new Consumer[JsonElement] {
            override def accept(t: JsonElement): Unit = {
                val result = JsonParser.parseString(t.toString).getAsJsonObject
                list.append(result)
            }
        }

        results.forEach(consumer)
        val resultList1 = list.toList.map { result: JsonObject =>
            val adId = result.get(ORIGINALITY_ID).getAsLong
            val conversionType = result.get(CONVERSION_TYPE).getAsInt
            val conversionTypeValue = result.get(CONVERSION_TYPE_VALUE).getAsString
            val targetValueDouble = result.get(TARGET_VALUE_DOUBLE).getAsDouble
            adId -> (conversionType, conversionTypeValue, targetValueDouble)
        }
        resultList1

这个示例用的Gson版本为2.8.2,依赖如下:

    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.2</version>
    </dependency>
3. 直接解析为实体类

实际使用中,发现用这种元组的方式,貌似没有直接用字段名的方式更容易理解,所以将json解析为实体类,貌似更人性化一些。

还是使用Gson2.8.7,我们写出了如下代码:

        val resultList2 = new ArrayBuffer[Result]()
        val consumer2 = new Consumer[JsonElement] {
            override def accept(t: JsonElement): Unit = {
                val result: Option[Result] = try {
                    Some(new Gson().fromJson(t.toString, classOf[Result]))
                } catch {
                    case _: Throwable => None
                }
                if (result.isDefined) resultList2.append(result.get)
            }
        }
        results.forEach(consumer2)

        resultList2.toList

其中,Result类是用Java实现的一个实体类,实现如下:

@Data
public class Result {
    @SerializedName(value = "adId", alternate = {"originalityId"})
    private long adId;

    @SerializedName("conversionType")
    private int conversionType;

    @SerializedName("conversionTypeValue")
    private String conversionTypeValue;

    @SerializedName("targetValueDouble")
    private String targetValueDouble;
}

这里解释一下为啥实体类Result是用Java实现的,而不是scala,其实是因为尝试了scalacase class,发现字段名不同的那些字段,使用@SerializedName貌似解决不了(json字符串里的字段名和实体类不一致,导致解析之后实体类字段值为0),具体原因待确定,如果有哪位老师,知道这其中的原因,辛苦留言解释一下,万谢!我的scala case class实现方式如下:

case class Result1(@SerializedName(value = "adId", alternate = Array[String]("originalityId")) adId: Long,
                   conversionType: Int,
                   conversionTypeValue: String,
                   targetValueDouble: Double)

三、总结

简单总结一些这次踩的坑吧:

1. 在scala中获取类类型

在使用Gson::fromJson方法的时候,第二个参数应该传Class<T>类类型,在scala获取类型,应该使用scala包下,Predef类里的classOf方法;而不能使用Class[T],其他的貌似也不行,诸如.class.getClass

2. 解决json字段名和实体类字段名不匹配的问题

使用@SerializedName注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface SerializedName {
    String value();

    String[] alternate() default {};
}

value: 序列化和反序列化的时候使用这个名称

alternate:英文直译:备用,当value指定的值不匹配的时候,会选取alternate指定列表里的名称,来映射(但在scala的case class中,好像失败了)。

四、完整代码

1. ParseJson.scala
package com.zhang.io

import com.google.gson.annotations.SerializedName
import com.google.gson.{Gson, JsonElement, JsonObject, JsonParser}
import com.zhang.models.Result

import java.util.function.Consumer
import scala.collection.mutable.ArrayBuffer
import scala.util.{Failure, Success, Try}

object ParseJson {
    val jsonStr =
        """
{
    "code":0,
    "message":"OK",
    "result":[
        {
            "originalityId":7,
            "conversionType":10011,
            "conversionTypeValue":"打点激活",
            "targetValueDouble":"1.0"
        }
    ]
}
          """

    private val CODE = "code"
    private val SUCCESS_CODE = 0
    private val MESSAGE = "message"
    private val RESULT = "result"
    private val ORIGINALITY_ID = "originalityId"
    private val CONVERSION_TYPE = "conversionType"
    private val CONVERSION_TYPE_VALUE = "conversionTypeValue"
    private val TARGET_VALUE_DOUBLE = "targetValueDouble"

    def main(args: Array[String]): Unit = {
        println(Array[String]("originalityId").mkString(", "))
        println(Array("originalityId").mkString(", "))
        Try(parseBody(Some(JsonParser.parseString(jsonStr).getAsJsonObject))) match {
            case Success(results) =>
                results.foreach(println)
            case Failure(exception) =>
                throw exception
        }
    }

    private def parseBody(bodyOpt: Option[JsonObject]) = {
        val isLegal = bodyOpt.isDefined && {
            val body = bodyOpt.get
            body.has(CODE) && body.get(CODE).getAsInt == SUCCESS_CODE && body.has(RESULT)
        }

        if (!isLegal) throw new RuntimeException(s"bodyOpt is illegal, bodyOpt: $bodyOpt")

        val body = bodyOpt.get
        val results = body.get(RESULT).getAsJsonArray

        val resultList = new ArrayBuffer[(Long, (Long, Int, String, Double))]()
        results.forEach(r => {
            val result = r.getAsJsonObject
            val adId = result.get(ORIGINALITY_ID).getAsLong
            val conversionType = result.get(CONVERSION_TYPE).getAsInt
            val conversionTypeValue = result.get(CONVERSION_TYPE_VALUE).getAsString
            val targetValueDouble = result.get(TARGET_VALUE_DOUBLE).getAsDouble
            resultList.append(adId -> (adId, conversionType, conversionTypeValue, targetValueDouble))
        })

        resultList.toList

        val list = new ArrayBuffer[JsonObject]()

        val consumer = new Consumer[JsonElement] {
            override def accept(t: JsonElement): Unit = {
                val result = JsonParser.parseString(t.toString).getAsJsonObject
                list.append(result)
            }
        }

        results.forEach(consumer)
        val resultList1 = list.toList.map { result: JsonObject =>
            val adId = result.get(ORIGINALITY_ID).getAsLong
            val conversionType = result.get(CONVERSION_TYPE).getAsInt
            val conversionTypeValue = result.get(CONVERSION_TYPE_VALUE).getAsString
            val targetValueDouble = result.get(TARGET_VALUE_DOUBLE).getAsDouble
            adId -> (conversionType, conversionTypeValue, targetValueDouble)
        }
        resultList1

        val resultList2 = new ArrayBuffer[Result]()
        val consumer2 = new Consumer[JsonElement] {
            override def accept(t: JsonElement): Unit = {
                val result: Option[Result] = try {
                    Some(new Gson().fromJson(t.toString, classOf[Result]))
                } catch {
                    case _: Throwable => None
                }
                if (result.isDefined) resultList2.append(result.get)
            }
        }
        results.forEach(consumer2)

        resultList2.toList
    }
}

case class Result1(@SerializedName(value = "adId", alternate = Array[String]("originalityId")) adId: Long,
                   conversionType: Int,
                   conversionTypeValue: String,
                   targetValueDouble: Double)
2. Result.java
package com.zhang.models;

import com.google.gson.annotations.SerializedName;
import lombok.Data;

@Data
public class Result {
    @SerializedName(value = "adId", alternate = {"originalityId"})
    private long adId;

    @SerializedName("conversionType")
    private int conversionType;

    @SerializedName("conversionTypeValue")
    private String conversionTypeValue;

    @SerializedName("targetValueDouble")
    private String targetValueDouble;
}

3. 使用到的依赖
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.7</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>

大致就这样吧,有问题欢迎留言哦,看到会尽快回复的 😃

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值