我认为这里有几个单独的问题需要解决,因此我列出了三种单独的方法。
TL;DR
要么正确使用 Jackson 多态性,要么根据您的情况,采用更简单的方法并消除对多态性的需要。看我的github上的代码.
1. 自定义解串器
您的 JSON 格式为:
{ inventory:
[ { productType: 'someProduct1',
details:
{ productId: 'Some_id',
description: 'some description' } },
{ productType: 'someProduct2',
details:
{ productId: 'Some_id',
description: { someKey: 'somevalue' }
}
}
]
}
场productType
在我看来,这是错误的,但如果这种格式是严格要求,那么您可以编写自己的反序列化器来查看productType
字段并实例化不同的具体类。
我认为这不是最好的解决方案,所以我没有编写示例代码,但我喜欢Joda 日期时间包作为自定义序列化/反序列化的参考
2. 杰克逊多态性
你们已经分开了Product
from ProductDetails
带有类型字段:
case class Product(productType:String,details:ProductDetails)
abstract class ProductDetails
我认为您已经混淆了 Jackson 的多态数据类型处理的工作原理,并因此使您的类设计变得复杂。
也许您的业务规则要求产品具有“类型”,在这种情况下,我会将其命名为“种类”或其他一些非代码标签,并将其放入您所说的内容中ProductDetails
.
但是,如果“类型”被包含在试图使类型多态性发挥作用的过程中,那么它就不是正确的方法。
我将以下内容作为 Scala 中 Jackson 多态性的工作示例:
/**
* The types here are close to the original question types but use
* Jackson annotations to mark the polymorphic JSON treatment.
*/
import scala.Array
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes(Array(
new Type(value = classOf[ProductDetailsSimple], name = "simple"),
new Type(value = classOf[ProductDetailsComplex], name = "complex")
))
abstract class Product
case class ProductDetailsSimple(productId: String, description: String) extends Product
case class ProductDetailsComplex(productId: String, description: Map[String, String]) extends Product
case class PolymorphicInventory(products: List[Product])
请注意,我删除了Product
vs ProductDetails
的区别,所以Inventory
现在只是作为一个列表Product
。我留下了名字ProductDetailsSimple
and ProductDetailsComplex
虽然我认为他们应该重新命名。
用法示例:
val inv = PolymorphicInventory(
List(
ProductDetailsSimple(productId="Some_id", description="some description"),
ProductDetailsComplex(productId="Some_id", description=Map("someKey" -> "somevalue"))
)
)
val s = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inv)
println("Polymorphic Inventory as JSON: "+s)
Output:
Polymorphic Inventory as JSON: {
"products" : [ {
"type" : "simple",
"productId" : "Some_id",
"description" : "some description"
}, {
"type" : "complex",
"productId" : "Some_id",
"description" : {
"someKey" : "somevalue"
}
} ]
}
3.去除多态性
我建议在这种情况下根本不需要多态性,并且错误在于尝试将“描述”制作为单个字符串或键/值映射,而它们实际上是具有不同意图的字段。
也许涉及到数据遗留问题(在这种情况下请参阅自定义 deser 建议),但如果数据在您的控制范围内,我投票支持“变得更简单”:
case class Product(productId: String,
description: String="",
attributes: Map[String, String]=Map.empty)
case class PlainInventory(products: List[Product])
我使用起来更“scala-rific”Option
表示缺少值,因此:
case class Product(productId: String,
description: Option[String]=None,
attributes: Option[Map[String, String]]=None)
用法示例:
val inv = PlainInventory(
List(
Product(productId="Some_id", description=Some("some description")),
Product(productId="Some_id", attributes=Some(Map("someKey" -> "somevalue")))
)
)
val s = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inv)
println("Plain Inventory as JSON: "+s)
Output:
Plain Inventory as JSON: {
"products" : [ {
"productId" : "Some_id",
"description" : "some description"
}, {
"productId" : "Some_id",
"attributes" : {
"someKey" : "somevalue"
}
} ]
}
工作最少的代码github.