Я бы настоятельно рекомендовал создавать ваши экземпляры композиционно - например, вместо того, чтобы запекать всю логику декодирования 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))