【问题标题】:Representing a list of JSON tuples as a case class field, with Json4s将 JSON 元组列表表示为案例类字段,使用 Json4s
【发布时间】:2013-11-23 00:47:36
【问题描述】:

鉴于以下 JSON:

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

将其表示为 Scala 案例类的最佳方式是什么?从逻辑上讲,它似乎应该是这样的:

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

但是,当我尝试用 Json4s 和 Jackson 解析这个时,我得到:

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

如果我构造我预期的Foo...

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

...把它写成 JSON,我没有得到我的元组列表,我得到了:

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

我在this answer 中看到,尽管 Json4s claims 在 DSL 中从 Tuple2s 生成对象,但它实际上不能为对象字段执行此操作,除非该对象字段被定义为 JValue。因为除了序列化和反序列化之外,我还想用我的Foo 模型对象做其他事情,所以我不想特别想用JValues 来定义它。

鉴于此,我该怎么办?

【问题讨论】:

    标签: json scala json4s


    【解决方案1】:

    我看到至少有两种方法可以处理这个问题:

    1. 使用地图列表
    2. 写一个自定义的(String, String)和JObject(JField(, JString())之间的转换函数

    有时直接在 AST 上工作或通过 values 函数提取纯 Map[String, Any] 也很有用。只要您使用 JValue DSL,Tuple2 转换就有效,但在对 Scala 类型进行提取/分解时则无效。

    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)))
    

    【讨论】:

    • 结束了编写自定义序列化程序。我认为这——“只要你使用 JValue DSL 就有效”——是关键的非显而易见的事情,尤其是。如果你不习惯 DSL-happy Scala 库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多