2013-07-15 7 views
7

Mi piacerebbe produrre JSON per un elenco che include sia classi base che classi derivate. Il codice seguente produce solo JSON per la classe Animal (non ottengo il campo di razza per i membri del tipo Dog). Qualche aiuto sarebbe apprezzato.Scala Play framework classi derivate 2.1

import play.api.libs.json._ 

class Animal (val name:String) { 
} 

object Animal { 
    implicit object animalWrite extends Writes[Animal] { 
    def writes(ts: Animal) = JsObject(Seq("name" -> JsString(ts.name))) 
    } 
} 

case class Dog (override val name:String, val breed: String) 
    extends Animal(name) { 
} 

object Dog { 
    implicit val format = Json.format[Dog] 
} 

case class Cat (override val name:String, val hairLength: Int) 
    extends Animal(name) { 
} 

object Cat { 
    implicit val format = Json.format[Cat] 
} 

object helloWorld extends App { 
// The list below outputs:  [{"name":"Ruff","breed":"labrador"}] 
// val l = List[Dog](Dog("Ruff", "labrador")) 

// The list below outputs:  [{"name":"Ruff"},{"name":"Fluffy"}] 
// I expect to see: [{"name":"Ruff","breed":"labrador"},{"name":"Fluffy","hairLength":3}] 
    val l = List[Animal](Dog("Ruff", "labrador"), Cat("Fluffy", 3)) 
    println(Json.toJson(l)) 
} 

Scala e Play newbie qui, per favore scusa uso inappropriato della terminologia.

risposta

5

L'API json utilizza estensivamente parametri impliciti, una caratteristica di Scala in cui è possibile fornire un elenco di parametri "implicito" e se non si specificano quei parametri il compilatore tenterà di trovare un oggetto nello scope corrente che è contrassegnato come implicito e corrisponde a tale firma.

Quindi, se per esempio potrebbe scrivere:

implicit val s = "my implicit string" 

def magicPrint(implicit message: String) { println(message) } 

// and then call it 
magicPrint 

Il compilatore selezionerebbe s per il messaggio di parametro in quanto è nella portata e ha il tipo corretto (String), così dopo la risoluzione implicita l'ultima riga del codice sarebbe in realtà più simile a questo

magicPrint(s) 

il Format/Writer è selezionato dal compilatore, in fase di compilazione, con un parametro implicito. Se si osserva la firma del metodo, toJson[A](item: A)(implicit writes: Writes[A]), è necessario un numero implicito Writes[A] che nel tuo caso è un Writes[List[Animal]] poiché List[Animal] è il tipo dell'elenco l. La riproduzione contiene un predefinito ha uno scrittore che si occupa della raccolta (DefaultWrites.traversableWrites) che a sua volta prende un implicito Writes[A] - nel tuo caso Writes[Animal], così il compilatore selezionerà e passerà i tuoi animalWrites.

che l'elenco contiene diversi tipi di animali è qualcosa che accade in fase di esecuzione e il compilatore non ha modo di sapere che dalle informazioni di tipo disponibile presso il Json.toJson(l)

Quindi, come vedete, non è possibile ottenere ciò che si vuole nel modo in cui si pensava, ma lo si può fare in quasi allo stesso modo, lasciando lo scrittore animale conoscere i sottotipi, per esempio:

implicit object animalWrite extends Writes[Animal] { 
    def writes(ts: Animal) = ts match { 
    // this will get an implicit Writes[Dog] since d is a Dog 
    case d: Dog => Json.toJson(d) 
    // this will get an implicit Writes[Cat] since c is a Cat 
    case c: Cat => Json.toJson(c) 
    case x => throw new RuntimeException(s"Unknown animal $x") 
    } 
} 

Spero che questo ha aiutato!

+0

Grazie johanandren. Questo è utile, ho pensato che il problema avesse a che fare con impliciti ma non ho capito come. Quello che ho fatto per risolvere il problema era implementare un metodo serialize in Dog and Cat che chiama 'Json.toJson (this)' e chiamarlo con 'l.map (_. Serialize) .mkString (" [",", ","] ")'. Il tuo è meglio perché non hai dovuto concatenare manualmente Json insieme come ho fatto io. Grazie. – ariscris

+0

is'nt meglio aggiungere una funzione toJSON in strega animali potrebbero essere attuate come quella '' ' caso classe Dog (sovrascrivere nome val: String, razza val: String) estende animali (nome) { def toJSON = JSON.TOJSON (this) '' ' – crak

+0

In genere si evita di farlo in quanto si mescolerebbe e accoppierà strettamente il modello di dominio con i dettagli di implementazione di una forma di serializzazione mentre le classi di tipi che riproducono json utilizza in genere ben disaccoppiamenti Questo. – johanandren