完整代码在最后哦,可以通过目录跳转
假设某个HTTP
接口的返回值如下,怎么在scala
中使用Gson
把result
解析出来呢?
{
"code":0,
"message":"OK",
"result":[
{
"originalityId":7,
"conversionType":10011,
"conversionTypeValue":"打点激活",
"targetValueDouble":"1.0"
}
]
}
本来的解法是这样的:
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
,依赖如下:
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<version>2.8.7version>
dependency>
但是上面的解法貌似只在高版本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.gsongroupId>
<artifactId>gsonartifactId>
<version>2.8.2version>
dependency>
实际使用中,发现用这种元组的方式,貌似没有直接用字段名的方式更容易理解,所以将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
,其实是因为尝试了scala
的case 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)
简单总结一些这次踩的坑吧:
在使用Gson::fromJson
方法的时候,第二个参数应该传Class
类类型,在scala
获取类型,应该使用scala
包下,Predef
类里的classOf
方法;而不能使用Class[T]
,其他的貌似也不行,诸如.class
,.getClass
。
使用@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中,好像失败了)。
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)
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;
}
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
<version>2.8.7version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.10version>
dependency>
大致就这样吧,有问题欢迎留言哦,看到会尽快回复的