Jackson Scala JSON反序列化到案例类 [英] Jackson Scala JSON Deserialization to case classes

查看:171
本文介绍了Jackson Scala JSON反序列化到案例类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个JSON,其格式如下:

I have a JSON which has following form:

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

我希望上面的json反序列化的案例类如下所示:

The case classes that I want the above json to deserialize look like following:

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模块反序列化上面的JSON字符串,如下所示:

I am using jackson-scala module to deserialize the above JSON string as follows:

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

我得到的错误如下:
意外的令牌(END_OBJECT),预期的FIELD_NAME:缺少属性'@details',包含类型id(for class ProductDetails)\ n at [来源:java.io.StringReader@12dfbabd; line:9,column:5]

The error I get is as follows: "Unexpected token (END_OBJECT), expected FIELD_NAME: missing property '@details' that is to contain type id (for class ProductDetails)\n at [Source: java.io.StringReader@12dfbabd; line: 9, column: 5]"

我已经通过关于多态反序列化的杰克逊文档并尝试了所提到的组合,但没有运气。
我想了解我在这里做错了什么,需要使用jackson模块进行反序列化修正。

I have been through jackson documentation on Polymorphic deserialization and have tried combinations as mentioned but with no luck. I would like to understand what I am doing wrong here, which needs correction with respect to deserialization using jackson module.

推荐答案

我认为这里有一些不同的问题要解决,所以我列出了三种不同的方法。

I think there's a few separate problems to address here, so I've listed three separate approaches.

要么正确使用Jackson多态性,要么在您的情况下,采用更简单的方法并消除对多态性的需要。请参阅我的 github上的代码

Either use Jackson polymorphism correctly or, in your case, go to a simpler approach and remove the need for the polymorphism. See my code on github.

您的格式化JSON是:

Your formatted JSON is:

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

在我看来,字段 productType 是错误的,但如果这种格式是严格要求那么你可以编写自己的反序列化器,查看 productType 字段并实例化一个不同的具体类。

The field productType is misplaced, in my opinion, but if this format is a strict requirement then you could write your own deserializer that looks at the productType field and instantiates a different concrete class.

我不认为这将是最好的解决方案所以我没有编写示例代码,但我喜欢 Joda日期时间包作为自定义序列化/反序列化的参考

I don't think this would be the best solution so I didn't write example code, but I like the Joda date-time package as a reference for custom serialize/deserialize

您已从 ProductDetails 中分离 Product 使用类型字段:

You've separated Product from ProductDetails with a type field:

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

abstract class ProductDetails

我认为你很困惑Jackson的多态数据类型处理如何工作并使你的类设计复杂化结果。

I think you've confused how Jackson's polymorphic data type handling works and complicated your class design as a result.

也许您的业务规则要求产品具有类型,在这种情况下,我将其命名为kind或其他一些非代码标签,并把它放入你所谓的 ProductDetails

Perhaps your business rules require that a product has a "type", in which case I'd name it "kind" or some other non-code label, and put it into what you've called ProductDetails.

但是如果试图让类型多态性工作时包含type,那么它就不是正确的方法。

But if "type" was included in an attempt to get type polymorphism working, then it isn't the right way.

我在下面作为Scala中Jackson多态性的工作示例包含了以下内容:

I've included the below as a working example of Jackson polymorphism in 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])

请注意,我删除了产品 vs ProductDetails 区别,因此库存现在就像产品的列表一样。我留下名称 ProductDetailsS​​imple ProductDetailsComplex 虽然我认为它们应该重命名。

Note that I removed the Product vs ProductDetails distinction, so an Inventory now just as a list of Product. I left the names ProductDetailsSimple and ProductDetailsComplex though I think they should be renamed.

示例用法:

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。删除多态性

我建议在这种情况下根本不需要多态,并且错误在于尝试使描述成为单个字符串或者键/值映射,当它们确实是具有不同意图的字段时。

3. Remove the polymorphism

I suggest that polymorphism in this case isn't needed at all, and that the error is in trying to make "description" either a single string or a key/value map when they are really fields with distinct intentions.

或许存在数据遗留问题(在这种情况下请参见自定义deser建议),但如果数据在您的控制范围内,我投票支持go simpler :

Perhaps there is a data legacy issue involved (in which case see the custom deser suggestion), but if the data is in your control, I vote for "go simpler":

case class Product(productId: String,
                   description: String="",
                   attributes: Map[String, String]=Map.empty)

case class PlainInventory(products: List[Product])

我使用选项表示缺少值更加scala-rific,所以:

I's more "scala-rific" to use Option to indicate the absence of a value, so:

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

这篇关于Jackson Scala JSON反序列化到案例类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆