【问题标题】:Rename JSON fields with circe用 circe 重命名 JSON 字段
【发布时间】:2017-03-11 09:45:54
【问题描述】:

我想在我的案例类和我的 JSON 中使用不同的字段名称,因此我需要一种舒适的方式来重命名编码和解码。

有人有好的解决方案吗?

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

    您可以使用Custom key mappings via annotations。最通用的方式是来自io.circe.generic.extras._JsonKey 注解。文档中的示例:

    import io.circe.generic.extras._, io.circe.syntax._
    
    implicit val config: Configuration = Configuration.default
    
    @ConfiguredJsonCodec case class Bar(@JsonKey("my-int") i: Int, s: String)
    
    Bar(13, "Qux").asJson
    // res5: io.circe.Json = JObject(object[my-int -> 13,s -> "Qux"])
    

    这需要包circe-generic-extras

    【讨论】:

      【解决方案2】:

      这是解码器的代码示例(有点冗长,因为它不会删除旧字段):

        val pimpedDecoder = deriveDecoder[PimpClass].prepare {
          _.withFocus {
            _.mapObject { x =>
              val value = x("old-field")
              value.map(x.add("new-field", _)).getOrElse(x)
            }
          }
        }
      

      【讨论】:

        【解决方案3】:
        implicit val decodeFieldType: Decoder[FieldType] =
          Decoder.forProduct5("nth", "isVLEncoded", "isSerialized", "isSigningField", "type")
                             (FieldType.apply)
        

        如果您有很多不同的字段名称,这是一种简单的方法。 https://circe.github.io/circe/codecs/custom-codecs.html

        【讨论】:

          【解决方案4】:

          您可以使用编码器上的mapJson 函数从通用编码器派生编码器并重新映射您的字段名称。

          您可以使用解码器上的prepare 函数将传递给通用解码器的 JSON 转换。

          你也可以从头开始写,但它可能是大量的样板,这些解决方案都应该是每个最多几行。

          【讨论】:

          • 在哪里可以找到更多使用 Circe 进行 JSON 模式演化的示例?例如Scala 模型中的新字段但 JSON 中没有,Scala 模型中删除的字段但 JSON 中的字段,重命名的字段等。
          • 我觉得官方文档挺好看的:circe.github.io/circe
          • 当进化变得越来越困难时,我们更喜欢将案例类拆分为通用模型 JSON 表示的版本。对于它们之间的转换,我们使用 Chimney 的宏:github.com/scalalandio/chimney 这比编写和支持自定义编解码器更简单、更安全。
          【解决方案5】:

          以下函数可用于重命名circe的JSON字段:

          import io.circe._
          
          object CirceUtil {
            def renameField(json: Json, fieldToRename: String, newName: String): Json =
              (for {
                value <- json.hcursor.downField(fieldToRename).focus
                newJson <- json.mapObject(_.add(newName, value)).hcursor.downField(fieldToRename).delete.top
              } yield newJson).getOrElse(json)
          }
          

          您可以像这样在Encoder 中使用它:

          implicit val circeEncoder: Encoder[YourCaseClass] = deriveEncoder[YourCaseClass].mapJson(
            CirceUtil.renameField(_, "old_field_name", "new_field_name")
          )
          

          额外

          单元测试

          import io.circe.parser._
          import org.specs2.mutable.Specification
          
          class CirceUtilSpec extends Specification {
          
            "CirceUtil" should {
              "renameField" should {
                "correctly rename field" in {
                  val json = parse("""{ "oldFieldName": 1 }""").toOption.get
                  val resultJson = CirceUtil.renameField(json, "oldFieldName", "newFieldName")
                  resultJson.hcursor.downField("oldFieldName").focus must beNone
                  resultJson.hcursor.downField("newFieldName").focus must beSome
                }
          
                "return unchanged json if field is not found" in {
                  val json = parse("""{ "oldFieldName": 1 }""").toOption.get
                  val resultJson = CirceUtil.renameField(json, "nonExistentField", "newFieldName")
                  resultJson must be equalTo json
                }
              }
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-05-09
            • 1970-01-01
            • 1970-01-01
            • 2019-09-18
            • 2018-03-10
            • 1970-01-01
            • 2021-07-14
            • 2017-03-31
            相关资源
            最近更新 更多