Deserialize nested polymorphic json field with json4s

I have a common problem, but I still can’t plunge into what I read.

In the scalatra application, I get the following json:

{ _type: "hello", timestamp: 123, data: [ {table: "stuffJ",_id: 24}, {table: "preferences",_id: 34,word: "john"} ]} 

with an unknown number of elements in field data. The field table will always be there to distinguish between types of classes. I am trying to figure it out with the RestAPIMessage class. This is what I have so far:

  implicit val jsonFormats = new DefaultFormats { outer => override val typeHintFieldName = "table" override val typeHints = ShortTypeHints(List(classOf[Preferences], classOf[StuffJ])) } sealed trait DataJson case class Preferences(table: String, _id: Long, word : String) extends DataJson case class StuffJ(table: String, _id: Long) extends DataJson case class RestAPIMessage(_type: String, timestamp: Long, data: List[DataJson]) // if sent as Json, returns a json with two "table" fields val message = new RestAPIMessage("hello", 123, List(new StuffJ("StuffJ", 24), new Preferences("preferences", 34, "john"))) // if received as Json, fails with a "no usable value for outer" val djson = """{"_type":"hello","timestamp":123,"data":[{"table":"StuffJ","_id":24},{"table":"table":"preferences","_id":34,"word":"john"}]}""" 

Thank you for your help!

0
source share
1 answer

Ok, I think I got it. In the end, it seems I could not have what I needed out of the box, and had to write my own serializer. I could not find a simple “polymorphic” example, so I summed up the minimal foobar example below:

 import org.json4s.{DefaultFormats, Formats} import org.json4s._ import org.json4s.JsonDSL._ import org.json4s.native.Serialization.{read, write} import org.json4s.native.Serialization sealed trait Bar case class Bar1(name : String, value: Int) extends Bar case class Bar2(name : String, stuff: Int) extends Bar case class Foo(timestamp:Long, bar: List[Bar]) var testFoo : Foo = new Foo(123, List(Bar1("bar1",1), Bar2("bar2",2))) object BarSerializer extends CustomSerializer[Bar](format => ( { case x: JObject => val name = (x \ "name").extract[String] name match { case "bar1" => val value = (x \ "value").extract[Int] Bar1(name,value) case "bar2" => val value = (x \ "stuff").extract[Int] Bar2(name,value) case x => throw new MappingException("Can't convert bar with name " + x + " to Bar") } }, { case x: Bar => //if you need only the deserializing part above, I think you could replace the below with write(x) x match { case Bar1(a,b) => ("name" -> a) ~ ("value" -> b) case Bar2(a,b) => ("name" -> a) ~ ("stuff" -> b) } } )) implicit val jsonFormats: Formats = Serialization.formats(NoTypeHints) + BarSerializer val a = write(testFoo) //returns : String = {"timestamp":123,"bar":[{"name":"bar1","value":1},{"name":"bar2","value":2}]} read[Foo](a) //returns : Foo = Foo(123,List(Bar1(bar1,1), Bar2(bar2,2))) 
+1
source

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


All Articles