How to serialize sealed abstract class with Json4s in Scala?

左心房为你撑大大i 提交于 2019-12-08 10:19:39

问题


How do i serialize a sealed abstract class with Json4s in Scala?

The following classes are defined:

sealed abstract class Person extends Product with Serializable
case class Spouse(name: String, age: Int) extends Person
case class Customer(name: String, age: Int, spouse: Spouse) extends Person

I create an object of type Customer:

val customer: Customer = Customer("Joe", 35, Spouse("Marilyn", 33))

Then I serialize to JSON:

implicit val formats = DefaultFormats
val serialized = write(customer)

That works fine. But then I try to deserialize:

val parsedObj = Serialization.read[Person](serialized)

Here I keep getting error:

org.json4s.package$MappingException: Parsed JSON values do not match with class constructor

I spent a lot of time trying to make this work...


回答1:


After a lot of googling around and a close read of the Json4s documentation i found out that I need a custom Serializer to make it work.

However, it took me a while to figure out that I need to set the formats to actually use the custom Serializer.

Here is the code that works for me.

Simple Example:

import org.json4s._
import org.json4s.native.JsonMethods._
import org.json4s.native.Serialization.{read, write}
import org.json4s.native.Serialization

sealed abstract class Person extends Product with Serializable
case class Spouse(name: String, age: Int) extends Person
case class Customer(name: String, age: Int, spouse: Spouse) extends Person

val customer: Customer = Customer("Joe", 35, Spouse("Marilyn", 33))

implicit val formats = Serialization.formats(NoTypeHints) + PersonSerializer
val serialized = write(customer)    
val parsedObj = Serialization.read[Person](serialized)

Custom Serializer:

object PersonSerializer extends Serializer[Person] {
    private val PersonClass = classOf[Person]

    def deserialize(implicit format: Formats)
    : PartialFunction[(TypeInfo, JValue), Person] = {
        case (TypeInfo(PersonClass, _), json) =>
            json match {
                case JObject(List(
                    JField("name", JString(d)),
                    JField("age", JInt(f)),
                    ("spouse", JObject(List(JField("name", JString(g)), JField("age", JInt(h)))))
                    )) => Customer(d, f.toInt, Spouse(g, h.toInt))
                case JObject(List(
                    JField("name", JString(d)),
                    JField("age", JInt(f))
                    )) => Spouse(d, f.toInt)
            }
    }

    def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
        case x: Customer =>
            JObject(List(
                JField("name", JString(x.name)),
                JField("age", JInt(x.age)),
                ("spouse", JObject(List(JField("name", JString(x.spouse.name)), JField("age", JInt(x.spouse.age)))))
            ))
        case x: Spouse =>
            JObject(List(
                JField("name", JString(x.name)),
                JField("age", JInt(x.age))
            ))
    }
}

Output

scala> val serialized = write(customer)

serialized: String = {"name":"Joe","age":35,"spouse":{"name":"Marilyn","age":33}}

scala> val parsedObj = Serialization.readPerson

parsedObj: Person = Customer(Joe,35,Spouse(Marilyn,33))



来源:https://stackoverflow.com/questions/49066877/how-to-serialize-sealed-abstract-class-with-json4s-in-scala

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!