3

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]

Mateusz Gruszczynski
  • 1,421
  • 10
  • 18

1 Answers1

2

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.

I See Voices
  • 842
  • 5
  • 13
  • 2
    Too be honest I've tried to use circe (which I previously replaced with json4s due to long compilation problem) but I find it too difficult to understand on my level of expertise in Scala. I've was using decoders from `io.circe.generic.auto._` but it seams those do not include java decoders. Do you know if there are decoders for java classes in circe or I would need to implement my own custom decoders if I decide to go back to circe? – Mateusz Gruszczynski Oct 19 '17 at 14:04
  • 2
    as I know `generic.auto._` uses conversion to `HList` and `Coproduct` from shapeless, which does not work for java classes. I am afraid you will need to implement your own decoders for java classes in circe. – I See Voices Oct 19 '17 at 14:17