使用circe重命名JSON字段

7

我希望在我的案例类和JSON中拥有不同的字段名称,因此我需要一种方便的重命名方式,在编码和解码中都能使用。

有人有好的解决方案吗?

5个回答

9
你可以使用通过注解自定义键映射来完成。最通用的方式是使用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 包。

请注意,如答案所述,您将不被允许在顶层声明implicit值。但是,您可以在底部添加类似以下内容的代码: implicit val config: Configuration = Configuration.default }``` - Bishwajit Purkaystha

2
implicit val decodeFieldType: Decoder[FieldType] =
  Decoder.forProduct5("nth", "isVLEncoded", "isSerialized", "isSigningField", "type")
                     (FieldType.apply)

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


2

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

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

1
你可以在 Encoder 上使用 mapJson 函数来从通用编码器中派生一个编码器,并重新映射你的字段名。
而你可以在 Decoder 上使用 prepare 函数将传递给它的 JSON 转换为通用解码器。
你也可以从头开始编写两者,但这可能需要大量的样板代码。这些解决方案应该都只有几行代码。

我在哪里可以找到更多使用Circe进行JSON模式演变的示例?例如,在Scala模型中添加新字段但不在JSON中,从Scala模型中删除字段但在JSON中存在,重命名字段等。 - pumpump
我认为官方文档非常好:https://circe.github.io/circe/ - C4stor
1
当演化变得更加困难时,我们更喜欢将案例类分割为常见模型和JSON表示的版本。为了在它们之间进行转换,我们使用Chimney的宏:https://github.com/scalalandio/chimney 这比编写和支持自定义编解码器要容易得多,也更安全。 - Andriy Plokhotnyuk

1
以下函数可用于重命名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)
}

您可以在编码器中使用它,如下所示:
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
      }
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接