【问题标题】:Parse Json from snake case to camel case将 Json 从蛇案例解析为骆驼案例
【发布时间】:2020-02-10 19:51:36
【问题描述】:

所以我有一个这种格式的 JSON,我试图将它从蛇形案例转换为与相应案例类匹配的骆驼案例

 val json = """
      {
        "items": [
        {
          "id": "7913",
          "route_id": "33",
          "predictable": true,
          "run_id": "33_486_1",
          "latitude": 34.0234949,
          "longitude": -118.398712,
          "heading": 236,
          "seconds_since_report": 59
        },
        {
          "id": "4140",
          "route_id": "76",
          "predictable": true,
          "run_id": "76_174_0",
          "latitude": 34.0331945,
          "longitude": -118.2646534,
          "heading": 122,
          "seconds_since_report": 12
        },
        {
          "id": "7620",
          "route_id": "20",
          "predictable": true,
          "run_id": "20_669_0",
          "latitude": 34.013733,
          "longitude": -118.490067,
          "heading": 334,
          "seconds_since_report": 172
        }
        ]
      }
      """.stripMargin

我想转换成的

  final case class Sample(
    id: Int,
    routeId: Int,
    predictable: Boolean,
    runId: String,
    latitude: Double,
    longitude: Double,
    heading: Int,
    secondsSinceReport: Int
  )

尝试使用

implicit val sampleDecoder = Decoder[List[Sample]].prepare(_.downField("items"))
val decodingResult = parser.decode(json)(sampleDecoder)

但结果是

Attempt to decode value on failed cursor: DownField(routeId),DownArray,DownField(items)

但是,如果我从案例类中注释掉带有驼峰案例的字段,我会得到

Sample(7913,true,34.0234949,-118.398712,236)
Sample(4140,true,34.0331945,-118.2646534,122)
Sample(7620,true,34.013733,-118.490067,334)

【问题讨论】:

    标签: scala circe


    【解决方案1】:

    您的用例直接来自文档:https://circe.github.io/circe/codecs/custom-codecs.html

    import io.circe.generic.extras._, io.circe.syntax._
    // import io.circe.generic.extras._
    // import io.circe.syntax._
    
    implicit val config: Configuration = Configuration.default.withSnakeCaseMemberNames
    // config: io.circe.generic.extras.Configuration = Configuration(io.circe.generic.extras.Configuration$$$Lambda$9172/0x0000000801132040@69e0f3f6,io.circe.generic.extras.Configuration$$$Lambda$9171/0x0000000801133040@66433b0e,false,None,false)
    
    @ConfiguredJsonCodec case class User(firstName: String, lastName: String)
    // defined class User
    // defined object User
    
    User("Foo", "McBar").asJson
    // res1: io.circe.Json =
    // {
    //   "first_name" : "Foo",
    //   "last_name" : "McBar"
    // }
    

    你需要 build.sbt 中的 generic-extras 依赖:

    libraryDependencies ++= Seq(
      "io.circe" %% "circe-core",
      "io.circe" %% "circe-generic",
      "io.circe" %% "circe-generic-extras",
      "io.circe" %% "circe-parser"
    ).map(_ % circeVersion)
    
    addCompilerPlugin("org.scalamacros" %% "paradise" % "2.1.1" cross CrossVersion.full)
    

    文档说您不需要 2.13.x 中的编译器插件,但我无法编译它。天堂还没有发布 2.13.x。因此,此解决方案仅适用于 2.12 及更早版本(尚未)。

    如果是一次性的,您可以手动进行转换,无需编译器宏:

    
       implicit val decodeSample: Decoder[Sample] = new Decoder[Sample] {
         final def apply(c: HCursor): Decoder.Result[Sample] =
           for {
             id <- c.downField("id").as[Int]
             routeId <- c.downField("route_id").as[Int]
             predictable <- c.downField("predictable").as[Boolean]
             runId <- c.downField("run_id").as[String]
             latitude <- c.downField("latitude").as[Double]
             longitude <- c.downField("longitude").as[Double]
             heading <- c.downField("heading").as[Int]
             secondsSinceReport <- c.downField("seconds_since_report").as[Int]
           } yield {
             new Sample(id, routeId, predictable, runId, latitude, longitude, heading, secondsSinceReport)
           }
       }
    

    【讨论】:

      【解决方案2】:

      如果你对另一个 JSON 库没问题,“play-json”就可以了。它是一个独立的库。

      final case class Sample(
          id: String,
          routeId: String,
          predictable: Boolean,
          runId: String,
          latitude: Double,
          longitude: Double,
          heading: Int,
          secondsSinceReport: Int)
      
      final case class Samples(items: Seq[Sample])
      
      import play.api.libs.json._
      
      implicit val jsonCaseConversion = JsonConfiguration(JsonNaming.SnakeCase)
      implicit val sampleJsonFormat = Json.format[Sample]
      implicit val samplesJsonFormat = Json.format[Samples]
      
      val json = """
        {
          "items": [
          {
            "id": "7913",
            "route_id": "33",
            "predictable": true,
            "run_id": "33_486_1",
            "latitude": 34.0234949,
            "longitude": -118.398712,
            "heading": 236,
            "seconds_since_report": 59
          },
          {
            "id": "4140",
            "route_id": "76",
            "predictable": true,
            "run_id": "76_174_0",
            "latitude": 34.0331945,
            "longitude": -118.2646534,
            "heading": 122,
            "seconds_since_report": 12
          },
          {
            "id": "7620",
            "route_id": "20",
            "predictable": true,
            "run_id": "20_669_0",
            "latitude": 34.013733,
            "longitude": -118.490067,
            "heading": 334,
            "seconds_since_report": 172
          }
          ]
        }
      """.stripMargin
      
      val optSamples = Json.parse(json).asOpt[Samples]
      

      我已将 Sample.id 更改为 String 类型。如果你希望这是Int,最简单的方法是添加这样的方法

      // this might thorw an exception.
      // maybe there is a reason why id in json is string
      def intId: Int = id.toInt
      

      【讨论】:

      【解决方案3】:

      终于不用了

      
      implicit val encoder : Encoder[Sample] = 
          Encoder.forProduct8(
            "id",
            "route_id",
            "predictable",
            "run_id",
            "latitude",
            "longitude",
            "heading",
            "seconds_since_report"
          )(Sample.unapply(_).get)
      
        implicit val decoder : Decoder[Sample] =
          Decoder.forProduct8(
            "id",
            "route_id",
            "predictable",
            "run_id",
            "latitude",
            "longitude",
            "heading",
            "seconds_since_report"
          )(Sample.apply)
      
      
      
         implicit val sampleDecoder = Decoder[List[Sample]].prepare(_.downField("items"))
         val decodingResult = parser.decode(json)(sampleDecoder)
      
         decodingResult match {
              case Left(value) => println(value.getMessage())
              case Right(value) => value.foreach(println)
         }
      
      

      并且结果与提供的 json 匹配

      Sample(7913,33,true,33_486_1,34.0234949,-118.398712,236,59)
      Sample(4140,76,true,76_174_0,34.0331945,-118.2646534,122,12)
      Sample(7620,20,true,20_669_0,34.013733,-118.490067,334,172)
      

      【讨论】:

        猜你喜欢
        • 2017-05-08
        • 1970-01-01
        • 1970-01-01
        • 2019-10-30
        • 1970-01-01
        • 2022-06-11
        • 2020-05-03
        • 2014-09-14
        • 1970-01-01
        相关资源
        最近更新 更多