Reputation: 9358
I'm trying to parse the following Json into a Scala object:
{
"oneOf": [
{ "$ref": "..." },
{ "$ref": "..." },
{ "$ref": "..." }
}
The field "oneOf" could also be "anyOf" or "allOf"; it will only be one of these values. I am constructing a case class, ComplexType, using Play's JSON library. The logic is simple; it looks for a given field and reads it if present, otherwise, checks a different field.
(json \ "allOf") match {
case a:JsArray => ComplexType("object", "allOf", a.as[Seq[JsObject]].flatMap(_.values.map(_.as[String])))
case _ =>
(json \ "anyOf") match {
case a:JsArray => ComplexType("object", "anyOf", a.as[Seq[JsObject]].flatMap(_.values.map(_.as[String])))
case _ =>
(json \ "oneOf") match {
case a:JsArray => ComplexType("object", "oneOf", a.as[Seq[JsObject]].flatMap(_.values.map(_.as[String])))
case _ => ComplexType("object", "oneOf", "Unspecified" :: Nil)
}
}
}
I'm not happy with this syntax; even though it works I don't see why I need to have nested match statements if no match is found. I believe a for-comprehension will work well: I can check for (json \ "allOf"), (json \ "oneOf) etc in a guard clause and yield the available result, but not sure how to get the syntax correct.
Is there a more elegant way to build this case class?
Thanks,
Mike
Upvotes: 1
Views: 503
Reputation: 38045
I don't think for-comprehension is useful here.
You could make your code more readable with custom extractors like this:
class Extractor(s: String) {
def unapply(v: JsValue): Option[JsArray] = json \ s match {
case a: JsArray => Some(a)
case _ => None
}
}
val AllOf = new Extractor("allOf")
val AnyOf = new Extractor("anyOf")
val OneOf = new Extractor("oneOf")
val (name, es) = json match {
case AllOf(a) => "allOf" -> Some(a)
case AnyOf(a) => "anyOf" -> Some(a)
case OneOf(a) => "oneOf" -> Some(a)
case _ => "oneOf" -> None
}
val result =
es.
map{ a => ComplexType("object", name, a.as[Seq[JsObject]].flatMap(_.values.map(_.as[String]))) }.
getOrElse("Unspecified" :: Nil)
Upvotes: 2