2014-12-11 7 views
6

Dalla console attivatore questo funziona:Gioca 2.3 opzioni di scrittura con Json.obj e getOrElse

scala> import play.api.libs.json._ 
import play.api.libs.json._ 

scala> val testVal = Some("foo") 
testVal: Some[String] = Some(foo) 

scala> Json.obj("myJson" -> testVal) 
res0: play.api.libs.json.JsObject = {"myJson":"foo"} 

Questo funziona anche:

scala> Json.obj("myJson" -> testVal.get) 
res3: play.api.libs.json.JsObject = {"myJson":"foo"} 

Questo fallisce:

scala> Json.obj("myJson" -> testVal.getOrElse("")) 
<console>:12: error: type mismatch; 
    found : Object 
    required: play.api.libs.json.Json.JsValueWrapper 
      Json.obj("myJson" -> testVal.getOrElse("")) 

Ma questo opere:

scala> val testVal2 = testVal.getOrElse("") 
testVal2: String = foo 

scala> Json.obj("myJson" -> testVal2) 
res2: play.api.libs.json.JsObject = {"myJson":"foo"} 

Perché il compilatore rifiuta il mio terzo esempio? testVal.getOrElse("") valuta una stringa, quindi perché il compilatore pensa che sia Object nel terzo esempio sopra?

risposta

5

Gli argomenti di Json.obj vengono ripetuti (String, JsValueWrapper) s. Quando passi qualche variabile, il compilatore tenterà di convertire implicitamente il tuo tipo in un JsValueWrapper utilizzando le conversioni definite nella libreria Play JSON (o Writes per i tuoi tipi).

Il problema qui è la controvarianza di getOrElse. Dal momento che la firma del getOrElse è in questo modo:

def getOrElse[B >: A](default: ⇒ B): B 

Questo significa che se si dispone di un Option[String], è possibile fornire un valore per getOrElse che non è un String, e il Option[String] diventa ora un Option[Any] più probabile. A causa di questa possibilità, il compilatore non cercherà una conversione implicita a JsValueWrapper e fallisce.

Il problema va via se si utilizza fold sul Option, che è invariante:

scala> val testVal = Some("foo") 
testVal: Some[String] = Some(foo) 

scala> Json.obj("myJson" -> testVal.fold("")(identity)) 
res7: play.api.libs.json.JsObject = {"myJson":"foo"} 
+0

Credo capire. Sono ancora un po 'confuso sul motivo per cui funziona nel mio ultimo esempio comunque. Perché il compilatore considera il valore "val" in modo diverso rispetto al semplice inserimento dell'espressione 'getOrElse' inline? Valutano la stessa cosa e non c'è dubbio che sarà sempre una stringa. – imagio

+2

Nell'ultimo esempio il tipo viene dedotto come 'String' dall'assegnazione. Ma in linea, il compilatore dedurrà il tipo solo dai parametri di tipo di 'getOrElse'. Ad esempio, non vedremo molto più avanti per vedere se c'è un 'String' letterale lì per vedere se può usare una conversione implicita. –

5

Si può anche contribuire a un metodo po getOrElse per definire direttamente il suo tipo di ritorno

Json.obj("myJson" -> testVal.getOrElse[String]("")) 
+0

Questa risposta è molto più leggibile dell'opzione 'testVal.fold'. – nVitius