Вопрос

Given the following JSON:

{
    "foo": "bar",
    "baz":[
        { "qux" : "quux" },
        { "quuux" : "quuuux" }
    ]
}

what's the best way to represent it as a Scala case class? Logically it seems as though it should be something like:

case class Foo(
  foo: String,
  baz: List[(String, String)]
)

However, when I try to parse this with Json4s and Jackson, I get:

No usable value for baz
No usable value for _1
Did not find value which can be converted into java.lang.String
org.json4s.package$MappingException: No usable value for baz
No usable value for _1
Did not find value which can be converted into java.lang.String

Going the other direction, if I construct my expected Foo...

val foo = Foo(foo = "bar", baz = List(("qux" -> "qux1"), ("qux" -> "qux2")))

...and write it as JSON, I don't get my list of tuples, I get:

{
  "foo" : "bar",
  "baz" : [ {
    "_1" : "qux",
    "_2" : "qux1"
  }, {
    "_1" : "qux",
    "_2" : "qux2"
  } ]
}

I see in this answer that despite Json4s claims to produce objects from Tuple2s in the DSL, it can't actually do it for an object field unless that object field is defined as a JValue. Since I want to do other things with my Foo model object besides serialize and deserialize it, I don't particularly want to define it in terms of JValues.

Given that, what should I be doing, here?

Это было полезно?

Решение

I see at least two options how to handle this:

  1. Use a List of Map
  2. Write a custom conversion function between (String, String) and JObject(JField(, JString())

Sometimes it is also useful working directly on the AST or extracting a plain Map[String, Any] via the values function. The Tuple2 conversion is valid as long as you are working with the JValue DSL, but not when doing extraction to/decomposition from Scala types.

import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonDSL._

val json = """{
             |    "foo": "bar",
             |    "baz":[
             |        { "qux" : "quux" },
             |        { "quuux" : "quuuux" }
             |    ]
             |}""".stripMargin

class StringTupleSerializer extends CustomSerializer[(String, String)](format => ( {
  case JObject(List(JField(k, JString(v)))) => (k, v)
}, {
  case (s: String, t: String) => (s -> t)
}))

implicit val formats = DefaultFormats + new StringTupleSerializer

case class FooMap(foo: String, baz: List[Map[String, String]])

case class FooTuple(foo: String, baz: List[(String, String)])

val ast = parse(json)

println(ast.extractOpt[FooMap])
//  Some(FooWithMap(bar,List(Map(qux -> quux), Map(quuux -> quuuux))))

println(ast.extractOpt[FooTuple])
//  Some(FooWithTuple(bar,List((qux,quux), (quuux,quuuux))))

val foo1 = FooMap(foo = "bar", baz = List(Map("qux" -> "qux1"), Map("qux" -> "qux2")))
val foo2 = FooTuple(foo = "bar", baz = List(("qux" -> "qux1"), ("qux" -> "qux2")))

println(pretty(Extraction.decompose(foo1)))
println(pretty(Extraction.decompose(foo2)))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top