【问题标题】:How to serialize key value pairs to a json array in scala?如何将键值对序列化为scala中的json数组?
【发布时间】:2020-11-11 10:38:12
【问题描述】:

假设我有多个键值对。每次运行时,键值对的数量及其内容都会发生变化。例如:

"favorite_food": "pizza"
"hobby": "running"

我想将这些转换成以下格式的json:

{
    "name": "John Doe",
    "fun facts" : [
        "favorite_food": "pizza",
        "hobby": "running"
    ]
}

请注意,键值对被方括号括起来。我正在尝试设置一些对象结构,以便将其序列化为这种格式。我试过了

import net.liftweb.json._
implicit val formats = DefaultFormats

case class Person(name: String, fun_facts: Map[String, String])

var john = Person("John Doe", Map("favorite_food" -> "pizza", "hobby" -> "running"))

val json = Serialization.write(john)

问题在于,它不是在有趣的事实周围使用方括号,而是使用花括号。 IE。 fun_facts: { ... }.

一个潜在的解决方案是将 Person 定义为 Person(name: String, fun_facts: String)。然后,我首先序列化我的键值对映射,使用serializedMap.replace("{", "[").replace("}", "]") 将花括号转换为方括号,并将该字符串保存在我将被序列化的 Person 对象中。但是,这需要我们丢失键值对的对象格式,如果我们想在准备返回 json 字符串之前使用它们,这可能会导致问题。

有没有更好的方法来做到这一点?我应该如何定义我的 Person 对象,以便它序列化为我需要的格式?

【问题讨论】:

    标签: json scala serialization


    【解决方案1】:

    虽然JSON spec 不允许将键值对序列化为 JSON 数组,但您可以使用 Map[String, String] 的自定义编解码器将其归档为 jsoniter-scala

    1. 为您的构建添加依赖项:
    libraryDependencies ++= Seq(
      "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core"   % "2.6.0",
      "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.6.0" % "provided" // it is required only in compile-time
    )
    
    1. 导入coremacros 模块:
    import com.github.plokhotnyuk.jsoniter_scala.macros._
    import com.github.plokhotnyuk.jsoniter_scala.core._
    
    1. 通过所需的注释来扩充您的数据结构(这里@named 用于更改fun_facts 字段的键):
    case class Person(name: String, @named("fun facts") fun_facts: Map[String, String])
    
    1. Map 创建一个自定义编解码器并为Person 派生一个编解码器:
    implicit val mapCodec: JsonValueCodec[Map[String, String]] = new JsonValueCodec[Map[String, String]] {
      override def decodeValue(in: JsonReader, default: Map[String, String]): Map[String, String] =
        if (in.isNextToken('[')) {
          if (in.isNextToken(']')) default
          else {
            in.rollbackToken()
            val mb = Map.newBuilder[String, String]
            var i = 0
            while ({
              mb += ((in.readKeyAsString(), in.readString(null)))
              i += 1
              if (i > 100000) { // a safe limit to avoid DoS attacks, see https://github.com/scala/bug/issues/11203
                in.decodeError("too many map inserts")
              }
              in.isNextToken(',')
            }) ()
            if (in.isCurrentToken(']')) mb.result()
            else in.arrayEndOrCommaError()
          }
        } else in.readNullOrTokenError(default, '[')
    
      override def encodeValue(kvs: Map[String, String], out: JsonWriter): Unit = {
        out.writeArrayStart()
        kvs.foreach { case (k, v) =>
          out.writeKey(k)
          out.writeVal(v)
        }
        out.writeArrayEnd()
      }
    
      override val nullValue: Map[String, String] = null
    }
    
    implicit val personCodec: JsonValueCodec[Person] = JsonCodecMaker.make
    
    1. 使用它们进行解析和序列化:
    val json = """{
                 |    "name": "John Doe",
                 |    "fun facts" : [
                 |        "favorite_food": "pizza",
                 |        "hobby": "running"
                 |    ]
                 |}""".stripMargin.getBytes("UTF-8")
    val person = readFromArray[Person](json)
    println(person)
    println()
    println(new String(writeToArray(person, config = WriterConfig.withIndentionStep(4)), "UTF-8"))
    

    预期输出:

    Person(John Doe,Map(favorite_food -> pizza, hobby -> running))
    
    {
        "name": "John Doe",
        "fun facts": [
            "favorite_food": "pizza",
            "hobby": "running"
        ]
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-19
      • 1970-01-01
      • 2021-11-13
      • 2016-08-03
      • 1970-01-01
      • 2021-05-29
      • 1970-01-01
      • 1970-01-01
      • 2011-09-10
      相关资源
      最近更新 更多