假设您有一个JSON,它看起来像这样:
[{"foo": 1, "bar": 2}, {"foo": 3, "bar": {"baz": 4}}]
使用Scala和类型来表示这一点似乎很自然:
sealed trait Item
case class IntItem(foo: Int, bar: Int) extends Item
case class Baz(baz: Int)
case class BazItem(foo: Int, bar: Baz) extends Item
我的问题是:是否可以使用Jackson的Scala模块将上面的JSON序列化为一个List[Item]
?
我的尝试:
val string = "[{\"foo\": 1, \"bar\": 2}, {\"foo\": 3, \"bar\": {\"baz\": 4}}]"
val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
mapper.readValue[List[Item]](string)
例外情况:
线程“main”com.fasterxml.jackson.databind.JsonMappingExctive中的异常:无法构造...项目的实例,问题:抽象类型需要映射到具体类型,具有自定义反序列化器,或者在[源:[{"foo": 1,"bar":{"baz": 2}},{"foo": 3,"bar":{"baz": 4}}]中使用其他类型信息实例化;行:1,列:2](通过引用链:com.fasterxml.jackson.module.scala.deser.BuilderWrapper[0])
这相当清楚地说明了问题所在,但我不确定如何最好地解决它。
正如@Dima所指出的,我不认为存在一个涵盖所有情况的通用解决方案。此外,我不确定它是否存在,因为差异可能隐藏得很深,我怀疑足够聪明的人可能会因此制造出一个停顿的问题。然而,许多具体案例都可以解决。
首先,如果您控制双方(序列化和反序列化),您应该考虑使用JsonTypeIdResolver
注释和一些TypeIdResolver
子类,这些子类将把类型的名称放在JSON本身中。
如果不能使用JsonTypeIdResolver
,可能唯一的解决方案就是按照错误提示推出定制的JsonDeserializer
。您在问题中提供的示例可以通过以下方式处理:
sealed trait Item
case class IntItem(foo: Int, bar: Int) extends Item
case class Baz(baz: Int)
case class BazItem(foo: Int, bar: Baz) extends Item
import com.fasterxml.jackson.core._
import com.fasterxml.jackson.databind._
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.util.TokenBuffer
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.node._
import com.fasterxml.jackson.databind.exc._
import java.io.IOException
class ItemDeserializer() extends StdDeserializer[Item](classOf[Item]) {
@throws[IOException]
@throws[JsonProcessingException]
def deserialize(jp: JsonParser, ctxt: DeserializationContext): Item = {
// 1) Buffer current state of the JsonParser
// 2) Use firstParser (from the buffer) to parser whole sub-tree into a generic JsonNode
// 3) Analyze tree to find out the real type to be parser
// 3) Using the buffer roll back history and create objectParser to parse the sub-tree as known type
val tb = new TokenBuffer(jp, ctxt)
tb.copyCurrentStructure(jp)
val firstParser = tb.asParser
firstParser.nextToken
val curNode = firstParser.getCodec.readTree[JsonNode](firstParser)
val objectParser = tb.asParser
objectParser.nextToken()
val bar = curNode.get("bar")
if (bar.isInstanceOf[IntNode]) {
objectParser.readValueAs[IntItem](classOf[IntItem])
}
else if (bar.isInstanceOf[ObjectNode]) {
objectParser.readValueAs[BazItem](classOf[BazItem])
}
else {
throw ctxt.reportBadDefinition[JsonMappingException](classOf[Item], "Unknown subtype of Item") // Jackson 2.9
//throw InvalidDefinitionException.from(jp, "Unknown subtype of Item", ctxt.constructType(classOf[Item])) // Jackson 2.8
}
}
}
然后你可以使用它如下
def test() = {
import com.fasterxml.jackson.module.scala._
import com.fasterxml.jackson.module.scala.experimental._
val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
// add our custom ItemDeserializer
val module = new SimpleModule
module.addDeserializer(classOf[Item], new ItemDeserializer)
mapper.registerModule(module)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val string = "[{\"foo\": 1, \"bar\": 2}, {\"foo\": 3, \"bar\": {\"baz\": 4}}]"
val list = mapper.readValue[List[Item]](string)
println(list.mkString(", "))
}
哪个指纹
第一项(1,2),第三项(3,第四项)
ItemDeserializer
中的主要技巧是使用TokenBuffer
两次解析JSON:第一次分析JSON树,找出应该解析为什么类型,第二次实际解析已知类型的对象。
我知道我可以创建一个单独的Report类,然后使用@JSONProperty将其嵌入到ReportResponse中。有没有一种方法可以避免这种情况,并用一个注释标记ReportResponse类,将它映射到JSON中的“Report”元素?
我对解析json有一个问题。它有一个日期为exapmle-"2014-01-07"。当它解析并成为createUserRequest.getBirthday()时,它包含-"2014-01-07T04:00:00.000 04:00"。我需要它在createUserRequest对象中,然后我会用另一个对象断言它。问题是如何得到“2014-01-07”? 在CreateUserRequest中,我
我使用Json-Jackson序列化/反序列化我的对象,并且与内置类配合得很好。 字符串、int等非常好。但是现在我必须序列化/反序列化以特定方式构建的对象。遵循声明: 如果我使用标准的序列化器/反序列化,它会向我显示一条错误消息,例如“java.lang.不支持的操作异常...”,因此它无法序列化/反序列化bobjs。 我如何以一般方式做到这一点,所以我不写3个序列化器/反序列化器,而是只告诉J
问题内容: 使用Jackson 2,我正在寻找一种 通用的 方式将对象序列化为单个值(然后序列化它们,然后再填充该单个字段),而不必重复创建JsonSerializer / JsonDeserializer来处理每种情况。@JsonIdentityInfo批注非常接近,但由于我知道,它将始终对完整的子对象进行序列化,因此略微遗漏了该标记。 这是我想做的一个例子。给定的类: 我希望Order可以序列
我收到来自第3方服务提供商的JSON响应,其中包含一系列对象。当我尝试使用Jackson api反序列化JSON时。我收到以下异常 我的回答是 我的POJO课是这样的 我正在尝试使用以下代码反序列化JSON 如果我试着去做 它在BEGIN_对象本身失败。 如何使用数组读取和反序列化JSON。我应该编写自己的反序列化器吗? 编辑如果我使用JSON字符串而不是流,那么我就能够取回所有Java对象。但为
我目前正在开发一个Java web应用程序,它使用Magento REST API公开的JSON数据。api返回的数据示例如下: 我的应用程序中有一个Java类,如下所示: 我想对数据进行反序列化,并将其转换为,但我总是得到以下错误: 这是我用来将JSON响应反序列化为ArrayList的语句行: 有人能分享一些见解吗?我看到一些例子,返回的JSON对象前面没有任何ID。那是因为我做错了什么吗?非