2014-03-05 5 views
12

Ich benutze Play-Jsons Makros, um implizite Writes für die Serialisierung von JSON zu definieren. Es sieht jedoch so aus, als ob play-json standardmäßig Felder auslässt, für die Option Felder auf None gesetzt sind. Gibt es eine Möglichkeit, den Standard so zu ändern, dass er stattdessen null ausgibt? Ich weiß, dass dies möglich ist, wenn ich meine eigene Writes Definition definiere, aber ich bin daran interessiert, dies über Makros zu tun, um den Standardcode zu reduzieren.Ausgabe 'Null' für Option [T] in Play-Json-Serialisierung, wenn Wert keine ist

Beispiel

case class Person(name: String, address: Option[String]) 

implicit val personWrites = Json.writes[Person]  
Json.toJson(Person("John Smith", None)) 

// Outputs: {"name":"John Smith"} 
// Instead want to output: {"name":"John Smith", "address": null} 

Antwort

13

Der Json.writes Makro erzeugt ein writeNullable[T] für optionale Felder. Wie Sie wissen (oder nicht), writeNullable[T] lässt das Feld weg, wenn der Wert None ist, während ein Nullfeld generiert.

Das Definieren eines benutzerdefinierten Schreibers ist die einzige Option, um dieses Verhalten zu erhalten.

( 
    (__ \ 'name).write[String] and 
    (__ \ 'address).write[Option[String]] 
)(unlift(Person.unapply _)) 
5

Keine echte Lösung für Sie Situation. Aber etwas besser, als die Schreibvorgänge manuell schreiben zu müssen

Ich habe eine Hilfsklasse erstellt, die Felder "sicherstellen" kann.

implicit class WritesOps[A](val self: Writes[A]) extends AnyVal { 
    def ensureField(fieldName: String, path: JsPath = __, value: JsValue = JsNull): Writes[A] = { 
     val update = path.json.update(
     __.read[JsObject].map(o => if(o.keys.contains(fieldName)) o else o ++ Json.obj(fieldName -> value)) 
    ) 
     self.transform(js => js.validate(update) match { 
     case JsSuccess(v,_) => v 
     case err: JsError => throw new JsResultException(err.errors) 
     }) 
    } 

    def ensureFields(fieldNames: String*)(value: JsValue = JsNull, path: JsPath = __): Writes[A] = 
     fieldNames.foldLeft(self)((w, fn) => w.ensureField(fn, path, value)) 

} 

, so dass Sie

Json.writes[Person].ensureFields("address")() 
0

ähnliche Antwort auf die oben schreiben können, aber ein andere Syntax für diesen:

implicit val personWrites = new Writes[Person] { 
    override def writes(p: Person) = Json.obj(
    "name" -> p.name, 
    "address" -> p.address, 
) 
} 
0

Dies ist einfach:

implicit val personWrites = new Writes[Person] { 
    override def writes(p: Person) = Json.obj(
    "name" -> p.name, 
    "address" -> noneToString(p.address), 
) 
} 

def optToString[T](opt: Option[T]) = 
    if (opt.isDefined) opt.get.toString else "null"