Невозможно создать объект, идущий через строку JSON с использованием анализатора Circe JSON - PullRequest
2 голосов
/ 24 апреля 2019

Итак, я пытаюсь создать собственный декодер для строки 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.

1 Ответ

4 голосов
/ 24 апреля 2019

Я бы настоятельно рекомендовал создавать ваши экземпляры композиционно - например, вместо того, чтобы запекать всю логику декодирования Headers в декодер RootInterface, вы можете определить отдельный экземпляр Decoder[Headers] и затем использовать его в своем Decoder[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Однако для определения класса case я настоятельно рекомендую использовать идиоматические имена членов Scala (т. е. верблюжий регистр, а не верхнюю змею).Комбинируя это с circe-дифференцированием , я получаю довольно ясное решение:

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))
...