【问题标题】:Cannot created object walking though JSON string using Circe JSON parser无法使用 Circe JSON 解析器创建遍历 JSON 字符串的对象
【发布时间】:2021-02-02 17:52:32
【问题描述】:

所以我正在尝试为要转换为域对象的 JSON 字符串创建自定义解码器。 我正在使用 Scala/Circe 遍历 JSON 并创建对象。 我无法让它运行。我确定我不清楚如何遍历 JSON。有人可以建议吗?

这是有问题的 JSON,

   {
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 
    "Accept-Encoding": "gzip, deflate", 
    "Accept-Language": "en-US,en;q=0.9", 
    "Host": "httpbin.org", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"
  }, 
  "origin": "61.16.136.118, 61.16.136.118", 
  "url": "https://httpbin.org/get"
}

这是我的代码的样子,

    import com.softwaremill.sttp._
import com.softwaremill.sttp.circe._
import io.circe._, io.circe.parser._

case class Headers(Accept: String, Accept_Encoding: String, Accept_Language: String, Host: String, Upgrade_Insecure_Requests: String, User_Agent: String)
case class RootInterface(args: String, headers: Headers, origin: String, url: String)

object SttpClientGetPost extends App {
  implicit val backend = HttpURLConnectionBackend()

  implicit val rootDecoder: Decoder[RootInterface] =
    (hCursor: HCursor) => {
      val tcursor = hCursor.downField("headers")
      val argsCursor = hCursor.downField("args")
        for{
          args <- for{
          testString <-  argsCursor.get[String]("args")
        }yield testString

        headers <- for {
          Accept <- tcursor.downField("Accept").as[String]
          Accept_Encoding <- tcursor.downField("Accept-Encoding").as[String]
          Accept_Language <- tcursor.downField("Accept-Language").as[String]
          Host <- tcursor.downField("Host").as[String]
          Upgrade_Insecure_Requests <- tcursor.downField("Upgrade-Insecure-Requests").as[String]
          User_Agent <- tcursor.downField("User-Agent").as[String]
        } yield Headers(Accept, Accept_Encoding, Accept_Language, Host, Upgrade_Insecure_Requests, User_Agent)
        origin <- hCursor.downField("Origin").as[String]
        url <- hCursor.downField("url").as[String]
      } yield RootInterface("", headers, origin, url)
    }
  val secondRequest = sttp //.headers(("userId", USER_ID),("password","testpassword"))
    .get(uri"http://httpbin.org/get")

  secondRequest.send().body match {
    case Left(fail) => System.out.println("The Get request was unsuccessful.   " + fail)
    case Right(rawJSONResponse) =>
      parser.decode(rawJSONResponse) match {
        case Left(fail) => System.out.println("The JSON response could not be parsed by Circe.  " + fail)
        case Right(rootInterfaceObject) =>
          System.out.println("Origin :" + rootInterfaceObject.origin)

      }
  }

}

更新: 我现在尝试这样做无济于事,

case class Headers(Accept: String, Accept_Encoding: String, Accept_Language: String, Host: String, Upgrade_Insecure_Requests: String, User_Agent: String)
case class RootInterface(args: String, headers: Headers, origin: String, url: String)


val doc = """{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 
    "Accept-Encoding": "gzip, deflate", 
    "Accept-Language": "en-US,en;q=0.9", 
    "Host": "httpbin.org", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"
  }, 
  "origin": "61.16.136.118, 61.16.136.118", 
  "url": "https://httpbin.org/get"
}"""


  implicit val decodeHeaders: Decoder[Headers] = Decoder.forProduct6(
  "Accept",
  "Accept-Encoding",
  "Accept-Language",
  "Host",
  "Upgrade-Insecure-Requests",
  "User-Agent"
 )(Headers(_, _, _, _, _, _))

implicit val decodeRootInterface: Decoder[RootInterface] = Decoder.forProduct4(
  "args",  
  "headers",
  "origin",
  "url"
)(RootInterface(_, _, _, _))

val t = decode[RootInterface](doc)

我仍然遇到解码失败。看起来像是因为args 字段。

【问题讨论】:

    标签: json scala circe


    【解决方案1】:

    我强烈建议您以组合方式构建您的实例——例如您可以定义一个单独的Decoder[Headers] 实例,然后在您的Decoder[RootInterface] 中使用它,而不是将所有Headers 解码逻辑烘焙到RootInterface 解码器中。

    我还建议避免直接使用游标,除非您正在做一些您知道需要该级别定义的特别复杂的事情。

    所以给出这个文件:

    val doc = """{
      "args": {}, 
      "headers": {
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 
        "Accept-Encoding": "gzip, deflate", 
        "Accept-Language": "en-US,en;q=0.9", 
        "Host": "httpbin.org", 
        "Upgrade-Insecure-Requests": "1", 
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"
      }, 
      "origin": "61.16.136.118, 61.16.136.118", 
      "url": "https://httpbin.org/get"
    }"""
    

    …以及这些案例类:

    case class Headers(Accept: String, Accept_Encoding: String, Accept_Language: String, Host: String, Upgrade_Insecure_Requests: String, User_Agent: String)
    case class RootInterface(args: String, headers: Headers, origin: String, url: String)
    

    ...以下是一个完整的工作示例,您可以在 REPL 中使用:

    import io.circe.Decoder, io.circe.jawn.decode
    
    implicit val decodeHeaders: Decoder[Headers] = Decoder.forProduct6(
      "Accept",
      "Accept-Encoding",
      "Accept-Language",
      "Host",
      "Upgrade-Insecure-Requests",
      "User-Agent"
     )(Headers(_, _, _, _, _, _))
    
    implicit val decodeRootInterface: Decoder[RootInterface] = Decoder.forProduct3(
      "headers",
      "origin",
      "url"
    )(RootInterface("", _, _, _))
    

    ……像这样:

    scala> decode[RootInterface](doc)
    res0: Either[io.circe.Error,RootInterface] = Right(RootInterface(,Headers(text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3,gzip, deflate,en-US,en;q=0.9,httpbin.org,1,Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36),61.16.136.118, 61.16.136.118,https://httpbin.org/get))
    

    (我不确定args 解码的意图是什么,所以我只是按照你的实现来使用空字符串。)

    但是,如果您控制 Headers 案例类定义,我强烈建议使用惯用的 Scala 成员名称(即驼峰式大小写,而不是大蛇形)。在我看来,将其与circe-derivation 结合起来可以得到一个非常清晰的解决方案:

    import io.circe.Decoder, io.circe.derivation.deriveDecoder
    
    case class Headers(
      accept: String,
      acceptEncoding: String,
      acceptLanguage: String,
      host: String,
      upgradeInsecureRequests: String,
      userAgent: String
    )
    
    object Headers {
      implicit val decodeHeaders: Decoder[Headers] = deriveDecoder(
        _.replaceAll("([A-Z])", "-$1").capitalize
      )
    }
    
    case class RootInterface(
      args: Map[String, String],
      headers: Headers,
      origin: String,
      url: String
    )
    
    object RootInterface {
      implicit val decodeRootInterface: Decoder[RootInterface] = deriveDecoder
    }
    

    然后:

    scala> io.circe.jawn.decode[RootInterface](doc)
    res0: Either[io.circe.Error,RootInterface] = Right(RootInterface(Map(),Headers(text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3,gzip, deflate,en-US,en;q=0.9,httpbin.org,1,Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36),61.16.136.118, 61.16.136.118,https://httpbin.org/get))
    

    【讨论】:

    • 非常感谢您提供的详细信息。我将尝试您提到的这两种方法并回复您。对于初学者,我不明白这是什么implicit val decodeHeaders: Decoder[Headers] = deriveDecoder( _.replaceAll("([A-Z])", "-$1").capitalize? ) 在做什么?
    • @SomBhattacharyya 它将成员名称从 Scala 惯用的驼峰式 (acceptHeader) 转换为传入的 Accept-Header 格式。
    • 我无法使第一种方式工作。用我的代码更新了我的问题。
    • 您将需要某种从 JSON 对象到字符串的映射,以获取 args 字段的值。你希望它看起来像什么?
    • 是的,我想我刚刚使用了 Map[String,String] args 映射。我使用的是String,这是不正确的。非常感谢您的热心帮助。
    猜你喜欢
    • 2020-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-30
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多