2013-08-02 38 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

同意。但是你提到這不是最好的解決方案,但我認爲這是最好的解決方案! :) –