2013-08-02 41 views
4

我有一个JSON它有以下形式:杰克逊斯卡拉JSON反序列化到case类

{ 
"inventory": [ 
      { 
     "productType": "someProduct1", 
     "details": { 
      "productId": "Some_id", 
      "description": "some description" 
     } 
     }, 
{ 
     "productType": "someProduct2", 
     "details": { 
      "productId": "Some_id", 
      "description":{"someKey":"somevalue"} 
     } 
    } 
] 
} 

,我想上面的JSON反序列化看起来像下面的情况下类:

case class Inventory(products:List[Product]) 
case class Product(productType:String,details:ProductDetails) 
abstract class ProductDetails 
case class ProductDetailsSimple(productId:String,description:String) extends ProductDetails 
case class ProductDetailsComplex(productId:String,description:Map[String,String]) extends ProductDetails 

我使用jackson-scala module反序列化上面的JSON字符串如下:

val mapper = new ObjectMapper() with ScalaObjectMapper 
mapper.registerModule(DefaultScalaModule) 
mapper.readValue(jsonBody, classOf[Inventory]) 

我得到的错误是如下所示: “意外的令牌(END_OBJECT),预期的FIELD_NAME:缺少属性'@details',它包含类型id(用于类ProductDetails)\ n在[Source:[email protected];行:9,列:5]“

我已经通过jackson documentation on Polymorphic deserialization,并尝试过组合,但没有运气 我想明白我在做什么错误,这需要纠正有关反序列化杰克逊模块

+0

你有这个工作吗? – NightWolf

回答

12

我认为有几个不同的问题来解决这里,所以我列出了三种不同的方法

TL; DR

要么正确使用杰克逊多态性或,你的情况,去到一个更简单的方法,并消除多态的需要米请参阅我的code on 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 date-time package作为自定义序列化的参考/反序列化

2.杰克逊多态性

你“VE分离ProductProductDetails利用类型字段:

case class Product(productType:String,details:ProductDetails) 

abstract class ProductDetails 

我想你混淆如何杰克逊的多态数据类型的处理工作和复杂的类设计的结果。

也许您的业务规则要求产品具有“类型”,在这种情况下,我会将其命名为“kind”或其他非代码标签,并将其放入您称为ProductDetails的内容中。

但是,如果“类型”被包含在试图获得类型多态性的工作中,那么它是不正确的。

我已经包含下面的Scala的杰克逊多态性的工作示例:

/** 
* 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列表。我留下了名字ProductDetailsSimpleProductDetailsComplex,但我认为他们应该重新命名。

实例:

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) 

输出:

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]) 

我的更“阶-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) 

输出:

Plain Inventory as JSON: { 
    "products" : [ { 
    "productId" : "Some_id", 
    "description" : "some description" 
    }, { 
    "productId" : "Some_id", 
    "attributes" : { 
     "someKey" : "somevalue" 
    } 
    } ] 
} 

github工作最少的代码。

+0

您的第二个解决方案修改了现有的JSON。当你无法改变JSON格式时,你如何处理它? –

+0

“格式”我认为你的意思是“结构”?我评论了保留解决方案1和3中的确切结构,基本上说要使用解决方案1.可能的解决方案2可以工作,但如果需要与某些非杰克逊友好的JSON完全一致,则自定义解串器是主要的我能想到的选项。 –

+0

同意。但是你提到这不是最好的解决方案,但我认为这是最好的解决方案! :) –