Jackson Scala JSON Deserialization to Class Classes

I have JSON which has the following form:

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

The classes of the classes that I want the json deserialized above to look like this:

 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 

I use the jacksonscala module to deserialize the above JSON string as follows:

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

The error I get is as follows: "Unexpected token (END_OBJECT) expected by FIELD_NAME: there is no property" @details ", which should contain the type identifier (for the ProductDetails class) \ n in [Source: java.io.StringReader@12dfbabd ; line: 9, column: 5] "

I went through Jackson's documentation on polymorphic deserialization and tried combinations, as mentioned, but no luck. I would like to understand what I'm doing wrong here, which requires correction regarding deserialization using the Jackson module.

+4
source share
1 answer

I think there are several separate issues here, so I have listed three separate approaches.

TL DR

Make the right use of Jackson polymorphism or, in your case, move to a simpler approach and eliminate the need for polymorphism. See my code on github .

1. Custom deserializer

Your formatted JSON:

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

The productType field productType inappropriate, in my opinion, but if this format is a strict requirement, you can write your own deserializer that looks in the productType field and creates an instance of another specific class.

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

2. Jackson Polymorphism

Separate Product from ProductDetails with a type field:

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

I think you are confused about how the processing of polymorphic data types in Jackson works, and as a result, the complex design of your class.

Perhaps your business rules require that the product has a “type”, in which case I would call it “good” or some other non-code, and put it in what you called ProductDetails .

But if "type" was included in an attempt to get typical polymorphism to work, then this is the wrong way.

I have included 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]) 

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

Usage example:

 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. Remove the polymorphism

I suggest that in this case polymorphism is not needed at all, and that the mistake is to try to make a “description” either as a single line or as a key / value map when they are really fields with different intentions.

Perhaps there is a problem of data obsolescence (in this case, see the hint for the user deserver), but if the data is under your control, I vote for "easier":

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

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

 case class Product(productId: String, description: Option[String]=None, attributes: Option[Map[String, String]]=None) 

Usage example:

 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" } } ] } 

Working minimal code on github .

+13
source

Source: https://habr.com/ru/post/1495042/


All Articles