JSON将Scala案例类序列化为仅字符串和整数

Posted

技术标签:

【中文标题】JSON将Scala案例类序列化为仅字符串和整数【英文标题】:JSON Serializing Scala Case Class to only strings and ints 【发布时间】:2022-01-03 04:25:09 【问题描述】:

我需要将少数案例类序列化为仅字符串和整数。意思是,如果有嵌套类型,它会被序列化为 JSON 对象的字符串化版本,而不是 JSON 对象。

例子:

case class Deepest(someNum: Int)
case class Inner(superDeep: Deepest)
case class Outer(aValue: Int, aNestedValue: Inner)

序列化Outer 的实例会导致(或类似的结果)


    "Outer": 
        "aValue": 5,
        "aNestedValue": " \"superDeep\": .... "
    

这可能吗?

【问题讨论】:

我猜有可能,例如,如果您在 Play-Json 中编写自定义 Writes[Outer] 【参考方案1】:

使用jsoniter-scala 是使用Scala 最简单、最有效的方法。

添加依赖:

libraryDependencies ++= Seq(
  // Use the %%% operator instead of %% for Scala.js  
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core"   % "2.12.0",
  // Use the "provided" scope instead when the "compile-internal" scope is not supported  
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.12.0" % "compile-internal"
)

使用来自这个 sn-p 的自定义编解码器:

import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._

object Example01 
  sealed trait Message
  case class Deepest(someNum: Int)
  case class Inner(superDeep: Deepest)
  case class Outer(aValue: Int, aNestedValue: Inner) extends Message

  implicit val innerCodec: JsonValueCodec[Inner] = new JsonValueCodec[Inner] 
    private val codec: JsonValueCodec[Inner] = JsonCodecMaker.make

    override def decodeValue(in: JsonReader, default: Inner): Inner =
      readFromStringReentrant(in.readString(null))(codec)

    override def encodeValue(x: Inner, out: JsonWriter): Unit =
      out.writeVal(writeToStringReentrant(x)(codec))

    override def nullValue: Inner = null
  
  implicit val messageCodec: JsonValueCodec[Message] =
    JsonCodecMaker.make(CodecMakerConfig.withDiscriminatorFieldName(None))

  def main(args: Array[String]): Unit = 
    val message = readFromString[Message](
    """
      |    "Outer": 
      |        "aValue": 5,
      |        "aNestedValue": " \"superDeep\":  \"someNum\": 1  "
      |    
      |""".stripMargin)
    println(writeToString[Message](message, WriterConfig.withIndentionStep(4)))
  

预期输出:


    "Outer": 
        "aValue": 5,
        "aNestedValue": "\"superDeep\":\"someNum\":1"
    

如果应该对多个类型进行字符串化,您可以定义一个函数,该函数采用常规编解码器并返回一个编解码器,该编解码器将原始类型的输出字符串化:

  def makeStringifyingCodec[A](codec: JsonValueCodec[A]): JsonValueCodec[A] = 
    new JsonValueCodec[A] 
      override def decodeValue(in: JsonReader, default: A): A =
        readFromStringReentrant(in.readString(null))(codec)

      override def encodeValue(x: A, out: JsonWriter): Unit =
        out.writeVal(writeToStringReentrant(x)(codec))

      override def nullValue: A = null.asInstanceOf[A]
    

并将其用于Outer 类的字段类型:

implicit val innerCodec: JsonValueCodec[Inner] = 
  makeStringifyingCodec(JsonCodecMaker.make[Inner])
implicit val messageCodec: JsonValueCodec[Message] = 
  JsonCodecMaker.make(CodecMakerConfig.withDiscriminatorFieldName(None))

【讨论】:

以上是关于JSON将Scala案例类序列化为仅字符串和整数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Scala 中将 Map 序列化为 JSON?

使用 circe 将 Scala None 编码为 JSON 值

将 Json 反序列化为实体框架无法将 int 转换为类型

如何使用 JSON-B 将 JSON 字符串反序列化为非公共类?

Scala使用circe将None编码为NaN json值

如何使用 Java 和 Gson 中的构建器模式将选择类字段序列化为 JSON 字符串?