Json 当多态类型在Seq()中时,使用Scala模块在Jackson中进行多态反序列化失败

Json 当多态类型在Seq()中时,使用Scala模块在Jackson中进行多态反序列化失败,json,scala,polymorphism,jackson,Json,Scala,Polymorphism,Jackson,在这个简单的例子中,第三个测试“反序列化”失败,并显示消息 Can not construct instance of com.egc.ost.pricing.contracts.response.A, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information a

在这个简单的例子中,第三个测试“反序列化”失败,并显示消息

Can not construct instance of com.egc.ost.pricing.contracts.response.A, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
 at [Source: java.io.StringReader@1f03691; line: 2, column: 29]
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.egc.ost.pricing.contracts.response.A, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
 at [Source: java.io.StringReader@1f03691; line: 2, column: 29] 
尽管第二个测试“反序列化”证明Jackson可以解析正确的多态类型

我正在使用Jackson版本和scala模块

  <dependency>
        <groupId>com.fasterxml.jackson.module</groupId>
        <artifactId>jackson-module-scala_2.10</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-joda</artifactId>
        <version>2.2.2</version>
    </dependency>

我相信这里有两个bug,一个与映射到Seq(tuple)相关

1) 映射不导出序列化的类型信息

2) Seq(Tuple)忽略反序列化的类型信息

解决方法:-使用包装类替换元组

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes(Array(
  new Type(value = classOf[B], name = "B"),
  new Type(value = classOf[C], name = "C")))
trait A{}

case class B(value : Double) extends A
case class C(value : String) extends A

case class ContainerWithMap( results: Map[String, A])
case class ContainerWithTupleSeq( results: Seq[(String, A)])
case class ContainerWithWrappedSeq( results: Seq[WrapperClass])

case class WrapperClass(s : String, a : A)

class ATest extends FlatSpec with ShouldMatchers {
  behavior of "A"
  val map: Map[String, A] = Map("ExampleB" -> B(1.0), "ExampleC" -> C("One"))
  val seq: Seq[WrapperClass] = Seq(WrapperClass("ExampleB", B(1.0)), WrapperClass( "ExampleC",C("One")))

  it should "fail not supporting reciprocal serialize de-serialize behaviour for maps"  in {
    val owner : ContainerWithMap = ContainerWithMap(map)
    val serialize: String = JsonMarshall.serialize(owner)
    println(serialize)      // types not exported on serialization 
    val thrown = evaluating{JsonMarshall.deserialize[ContainerWithMap](serialize)} should produce [JsonMappingException]
    thrown.getMessage should startWith("Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'type' that is to contain type id")
  }

  it should "fail not supporting reciprocal serialize de-serialize behaviour for sequence of tuples"   in {
    val owner : ContainerWithTupleSeq = ContainerWithTupleSeq(map.toSeq)
    val serialize: String = JsonMarshall.serialize(owner)
    println(serialize)     // types ignored on de-Serialization
    val thrown = evaluating{JsonMarshall.deserialize[ContainerWithTupleSeq](serialize)} should produce [JsonMappingException]
    thrown.getMessage should startWith("Can not construct instance of com.egc.ost.pricing.contracts.response.A, problem: " +
      "abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information")
  }

  it should "work if using a wrapper class"  in {
    val owner : ContainerWithWrappedSeq = ContainerWithWrappedSeq(seq)
    val serialize: String = JsonMarshall.serialize(owner)
    println(serialize)
    val deserialize: ContainerWithWrappedSeq = JsonMarshall.deserialize[ContainerWithWrappedSeq](serialize)
    println(deserialize)
    deserialize should be(owner)
  }

}

这个问题的解决方案出现在版本2.2.3中。使用这个版本可以避免使用包装类。

有趣的是,如果我用一个映射[String,A]替换Seq[(String,A)],并将json更改为{“results”:{“ExampleB”:{“type”:“B”,“value”:1.0},“ExampleC”:{“type”:“C”,“value”:“One”}}它确实反序列化,但是。。。序列化时,它会删除类型信息。在我看来,它像一只虫子。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes(Array(
  new Type(value = classOf[B], name = "B"),
  new Type(value = classOf[C], name = "C")))
trait A{}

case class B(value : Double) extends A
case class C(value : String) extends A

case class ContainerWithMap( results: Map[String, A])
case class ContainerWithTupleSeq( results: Seq[(String, A)])
case class ContainerWithWrappedSeq( results: Seq[WrapperClass])

case class WrapperClass(s : String, a : A)

class ATest extends FlatSpec with ShouldMatchers {
  behavior of "A"
  val map: Map[String, A] = Map("ExampleB" -> B(1.0), "ExampleC" -> C("One"))
  val seq: Seq[WrapperClass] = Seq(WrapperClass("ExampleB", B(1.0)), WrapperClass( "ExampleC",C("One")))

  it should "fail not supporting reciprocal serialize de-serialize behaviour for maps"  in {
    val owner : ContainerWithMap = ContainerWithMap(map)
    val serialize: String = JsonMarshall.serialize(owner)
    println(serialize)      // types not exported on serialization 
    val thrown = evaluating{JsonMarshall.deserialize[ContainerWithMap](serialize)} should produce [JsonMappingException]
    thrown.getMessage should startWith("Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'type' that is to contain type id")
  }

  it should "fail not supporting reciprocal serialize de-serialize behaviour for sequence of tuples"   in {
    val owner : ContainerWithTupleSeq = ContainerWithTupleSeq(map.toSeq)
    val serialize: String = JsonMarshall.serialize(owner)
    println(serialize)     // types ignored on de-Serialization
    val thrown = evaluating{JsonMarshall.deserialize[ContainerWithTupleSeq](serialize)} should produce [JsonMappingException]
    thrown.getMessage should startWith("Can not construct instance of com.egc.ost.pricing.contracts.response.A, problem: " +
      "abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information")
  }

  it should "work if using a wrapper class"  in {
    val owner : ContainerWithWrappedSeq = ContainerWithWrappedSeq(seq)
    val serialize: String = JsonMarshall.serialize(owner)
    println(serialize)
    val deserialize: ContainerWithWrappedSeq = JsonMarshall.deserialize[ContainerWithWrappedSeq](serialize)
    println(deserialize)
    deserialize should be(owner)
  }

}