Mateusz Gruszczynski
Mateusz Gruszczynski

Reputation: 1510

Deserializing to java objects with Scala and json4s

I have HTTP client written in Scala that uses json4s/jackson to serialize and deserialize HTTP payloads. For now I was using only Scala case classes as model and everything was working fine, but now I have to communicate with third party service. They provided me with their own model but its written in Java, so now I need to deserialize jsons also to Java classes. It seams to work fine with simple classes but when class contains collections like Lists or Maps json4s has problems and sets all such fields to null.

Is there any way to handle such cases? Maybe I should use different formats (I'm using DefaultFormats + few custom ones). Example of problem with test:

import org.json4s.DefaultFormats
import org.json4s.jackson.Serialization.read
import org.scalatest.{FlatSpec, Matchers}

class JavaListTest extends FlatSpec with Matchers{

    implicit val formats = DefaultFormats

    "Java List" should "be deserialized properly" in {
        val input = """{"list":["a", "b", "c"]}"""
        val output = read[ObjectWithList](input)
        output.list.size() shouldBe 3
    }
}

And sample Java class:

import java.util.List;

public class ObjectWithList {
    List<String> list;
}

I have also noticed that when I'll try to deserialize to Scala case class that contains java.util.List[String] type of field I'll get an exception of type: org.json4s.package$MappingException: Expected collection but got List[String]

Upvotes: 3

Views: 1637

Answers (1)

I See Voices
I See Voices

Reputation: 852

Key for solving your issue, is composition of formatters. Basically you want to define JList formatter as list formatter composed with toJList function. Unfortunately, json4s Formatters are extremely difficult to compose, so I used the Readers for you to get an idea. I also simplified an example, to having only java list:

import DefaultReaders._
import scala.collection.JavaConverters._

implicit def javaListReader[A: Reader]: Reader[java.util.List[A]] = new Reader[util.List[A]] {
        override def read(value: JValue) = DefaultReaders.traversableReader[List, A].read(value).asJava
}

val input = """["a", "b", "c"]"""
val output = Formats.read[java.util.List[String]](parse(input))

To my knowledge json4s readers will not work with java classes out of the box, so you might either need to implement the Serializer[JList[_]] the same way, or mirror your java classes with case classes and use them inside your domain.

P.S. Highly recommend you to switch to circe or argonaut, then you will forget about the most problems with jsons.

Upvotes: 2

Related Questions