2016-09-28 1 views
0

Ich verwende derzeit den Play-Framework-JSON-Parser, um eine JSON-Zeichenfolge in meinem Scala-Code zu analysieren.Dynamische JSON-Werte in Play Framework JSON

Ich habe die folgende Klasse:

case class Address(address: String, 
        gps: GPS, 
        country: String) {} 

object Address {  
    implicit val reads: Reads[Address] = (
     (JsPath \ "address").read[String] and 
     (JsPath \ "gps").read[GPS] and 
     (JsPath \ "country").read[String] 
    ) (Address.apply _) 

    implicit val writes: Writes[Address] = (
     (JsPath \ "address").write[String] and 
     (JsPath \ "gps").write[GPS] and 
     (JsPath \ "country").write[String] 
    ) (unlift(Address.unapply)) 
} 

die mit folgenden json gut funktioniert:

{ 
    "address": "123 Fake Street", 
    "country": "USA", 
    "gps": { ... } 
} 

Das Problem ist, dass die json in einigen Situationen anstelle der GPS-Feld eine Zeichenfolge sein kann was nicht analysiert, dh

{ 
    "address": "123 Fake Street", 
    "country": "USA", 
    "gps": "123abc" 
} 

Jetzt weiß ich, dass ich das GPS-Mitglied nicht b sein kann Ob eine Zeichenfolge oder ein GPS-Objekt, aber gibt es eine Möglichkeit, eine Option [GPS] zu sagen und nur einen Wert zu haben, wenn der JSON ein GPS-Objekt enthält?

+0

nicht wirklich Understaning Ihre Frage, aber ich habe einen Kommentar auf Play-json und es kann Ihnen helfen, sich auf der Lösung Ihre Probleme: http://pedrorijo.com/blog/scala-json/ und http://pedrorijo.com/blog/scala-json-part2/ – pedrorijo91

Antwort

2

Nur sehr wenig wird benötigt, um in Ihrem impl zu ändern. Sie müssen das Feld "gps" als etwas lesen, das wie JsValue "sicher" ist, und dann versuchen, es in Ihre GPS-Fallklasse zu mappen, falls dies möglich ist. Wenn nicht, geben Sie None zurück.

case class GPS(a:String, b:String) 
object GPS { 
    val travelInfoReads = Json.reads[GPS] 
    val travelInfoWrites = Json.writes[GPS] 
    implicit val travelInfoFormat: Format[GPS] = Format(travelInfoReads, travelInfoWrites) 
} 
case class Address(address: String, 
        gps: Option[GPS], 
        country: String) {} 

object Address { 
    implicit val reads: Reads[Address] = (
    (JsPath \ "address").read[String] and 
     (JsPath \ "gps").read[JsValue].map(js => js.asOpt[GPS]) and 
     (JsPath \ "country").read[String] 
    ) (Address.apply _) 

    implicit val writes: Writes[Address] = (
    (JsPath \ "address").write[String] and 
     (JsPath \ "gps").writeNullable[GPS] and 
     (JsPath \ "country").write[String] 
    ) (unlift(Address.unapply)) 
} 

Getestet habe ich es auch:

val json = Json.toJson(Address("1",Some(GPS("a","b")),"2")) 
     println(json) 
     println(json.as[Address]) 
     val newObj: JsObject = (json.as[JsObject] - "gps") + ("gps" -> JsNumber(1)) 
     println(newObj) 
     val a = newObj.as[Address] 
     println(a) 
     a must beEqualTo(Address("1",None,"2")) 

Ausgang war wie

{"address":"1","gps":{"a":"a","b":"b"},"country":"2"} 
Address(1,Some(GPS(a,b)),2) 
{"address":"1","country":"2","gps":1} 
Address(1,None,2) 
+0

Wow, das funktioniert super! Ich habe jedoch Ihr getestetes Beispiel versucht: 'val newObj: JsObject = (json.as [JsObject] -" gps ") + (" gps "-> JsNumber (1))' und dies funktioniert in diesem Beispiel, in meinem Fall Mein JSON ist so wie {Adresse: {gps: {...}}}, weißt du, ob es überhaupt vorhanden ist, um es in das innere Element zu injizieren? – RichyHBM

+0

Ich bin mir nicht sicher, was du meinst. Kannst du mehr jsons als Beispiel schreiben? –

+0

Ich meinte, wenn statt '{ " Adresse ":" 123 gefälschte Straße ", " Land ":" USA ", " gps ": {...} }' der Json war eigentlich: '{" Adresse ": { " Adresse ":" 123 Fake Street ", " Land ":" USA ", " gps ": {...} } }' aber ich erkannte, dass ich tun konnte: 'val gps: JsObject = Json.parse ("" {"Adresse": {"gps": {...}}} "" "). Als [JsObject]; val json = Json.parse (address.toJson()). as [JsObject] .deepMerge (gps) ' – RichyHBM