这是解决您的问题的一种非常冗长的方法,但我希望它具有让您识别甚至纠正您可能需要的每个极限情况的优势:
import io.circe._
import io.circe.parser.parse
case class Response(technicalData: Seq[TechnicalData])
case class TechnicalData(techValueString: String)
val stringAsJson1 = """{
"technicalData": {}
}"""
val stringAsJson2 = """{
"technicalData": [
{
"techValueString": "0.173"
},
{
"techValueString": 0.173
}
]
}"""
def manageTechnicalDataAsArray(jsonArray: Vector[io.circe.Json]): Response = {
Response(
jsonArray.map(cell => {
val value = cell.asObject
.getOrElse(throw new Exception("technicalData as a array should have each cell as an object"))
.apply("techValueString")
.getOrElse(throw new Exception("techValueString should be a key of any cell under technicalData array"))
TechnicalData(value.asNumber
.map(_.toString)
.getOrElse(
value.asString
.getOrElse(throw new Exception("techValueString value should be either string or number"))
)
)
}
)
)
}
def manageTechnicalDataAsObject(jsonObject: io.circe.JsonObject): Response = {
jsonObject.toIterable match {
case empty if empty.isEmpty => Response(Nil)
case _ => throw new Exception("technicalData when object should be empty")
}
}
def parseResponse(jsonAsString: String): Response = {
parse(jsonAsString).getOrElse(Json.Null)
.asObject
.map(_("technicalData")
.getOrElse(throw new Exception("the json should contain a technicalData key"))
.arrayOrObject(throw new Exception("technicalData should contain either an objet or array"),
manageTechnicalDataAsArray,
manageTechnicalDataAsObject
)
).getOrElse(throw new Exception("the json should contain an object at top"))
}
println(parseResponse(stringAsJson1))
println(parseResponse(stringAsJson2))
我可能很快会提供一个较短的版本,但对限制情况的指示性较差。您可以使用您的好 json 的调整版本来探索它们。
希望对你有帮助。
编辑:这是一个比上面更短、更清洁的解决方案,它是在@Sergey Terentyev 很好地找到一个之后出现的。好吧,它可能以某种方式不太可读,但它倾向于用或多或少的方式来处理限制情况:
// Structure part
case class TechnicalData(techValueString: String)
object TechnicalData {
def apply[T](input: T) = new TechnicalData(input.toString)
}
case class Response(technicalData: Seq[TechnicalData])
// Decoding part
import io.circe.{Decoder, parser, JsonObject, JsonNumber}
import io.circe.Decoder.{decodeString, decodeJsonNumber}
def tDDGenerator[C : Decoder]: Decoder[TechnicalData] = Decoder.forProduct1("techValueString")(TechnicalData.apply[C])
implicit val technicalDataDecoder: Decoder[TechnicalData] = tDDGenerator[String].or(tDDGenerator[JsonNumber])
implicit val responseDecoder: Decoder[Response] = Decoder[JsonObject]
.emap(_("technicalData").map(o => Right(o.as[Seq[TechnicalData]].fold(_ => Nil, identity)))
.getOrElse(Right(Nil))
.map(Response.apply))
// Test part
val inputStrings = Seq(
"""{
| "technicalData": [
| {
| "techValueString": "0.173"
| },
| {
| "techValueString": 0.173
| }
| ]
|}
""".stripMargin,
"""{
| "technicalData": {}
|}
""".stripMargin
)
inputStrings.foreach(parser.decode[Response](_).fold(println,println))