2013-04-10 6 views
7
import net.liftweb.json._ 
import net.liftweb.json.JsonParser._ 

object test02 extends App { 
    implicit val formats = DefaultFormats 
    case class User(
     id: Int = 0, 
     name: String = "John Doe", 
     gender: String = "M") 

    val s1=""" {"id":1,"name":"Bill","gender":"M"} """ 
    var r1=Serialization.read[User](s1) 
    println(r1) 

    val s2=""" {"id":1} """ 
    var r2=Serialization.read[User](s2) 
    println(r2) 

} 

Die zweite Serialization.read verursacht Ausnahme: net.liftweb.json.MappingException: Kein verwendbarer Wert für name.Wie füllen Sie Fallklasse von Json mit Teildaten?

Wie könnte ich möglicherweise Daten von JSON in Case-Klasse lesen, aber wenn einige Felder fehlen, werden sie durch Standardwerte aus Case-Klasse ersetzt?

+0

Antwort, wie dies mit Play JSON-Bibliothek ist auch akzeptabel. – codez

Antwort

5

Sieht aus wie ein offenes Ticket für dieses eine ganze Weile gewesen: https://www.assembla.com/spaces/liftweb/tickets/534

In der Zwischenzeit ist eine Option, ein Option zu verwenden:

case class User(
     id: Int = 0, 
     name: Option[String], 
     gender: Option[String]) { 
     def defaults = copy(
     name = name orElse Some("John Doe"), 
     gender = gender orElse Some("M")) 
    } 
    // ...   
    val s2=""" {"id":1} """ 
    var r2=Serialization.read[User](s2) 
    println(r2) 

Das sollten Ihnen geben:

User(1,None,None) 

Und Sie könnten so etwas verwenden, um Standardwerte auszufüllen:

val r2 = Serialization.read[User](s2).defaults 

// r2: User = User(1,Some(John Doe),Some(M)) 

Die andere Option ist eine zusätzliche Konstrukteuren für Ihren Fall Klasse verwendet wird:

case class User(id: Int, name: String, gender: String) 
object User { 
    def apply(id:Int): User = User(id, "John Doe", "M") 
} 
0

meisten scala JSON-Bibliotheken ersticken zu diesem Thema.

Wir verwenden eine interne JSON-Bibliothek, die Makros verwendet, um fehlerfreie Standardwerte für fehlende Felder bereitzustellen. (auch zwischen Null als keine und Undefined als Standard. So könnte man eine Option mit einem Standard von Einige haben, wenn Sie wollten)

In der Hoffnung, es bald opensource.

EDIT: im Grunde kenne ich keine anderen, die dies tun. aber es ist ein sich ständig änderndes Ökosystem, neugierig, ob noch jemand es angegangen hat

5

Wie es mit dem Spiel json Bibliothek zu tun, obwohl, müssen Sie die Standardeinstellungen im Parser und nicht nur als Standard für die Werte:

case class User(id: Int, name: String, gender: String) 

import play.api.libs.json._ 
import play.api.libs.functional.syntax._ 

implicit val userReads: Reads[User] = (
    (__ \ "id").read[Int] and 
    (__ \ "name").read[String].or(Reads.pure("John Doe")) and 
    (__ \ "gender").read[String].or(Reads.pure("Male")) 
)(User) 

Json.fromJson[User](Json.parse("""{"id":1,"name":"Bill","gender":"M"}""")) 

Json.fromJson[User](Json.parse("""{"id":1}""")) 

ich denke, Sie die Standardwerte aus dem Standardparameter gibt, indem eine Vorlage Instanz Benutzer schicken und dann den Standard von jedem Feld Reads.pure statt hartzucodieren eine Zeichenfolge liefern könnten.

4

Hier ist eine Play-Lösung, bei der Sie die Standardwerte nicht zweimal oder an einem seltsamen Ort angeben müssen - sie verwendet einen Makro, um den entsprechenden Standard zur Kompilierzeit zu finden.

Zuerst für den Fall Klasse:

case class User(id: Int = 0, name: String = "John Doe", gender: String = "M") 

Als nächstes müssen Sie DefaultFinder definieren, wie ich in this blog post beschreiben. Dann bist du praktisch getan:

import DefaultFinder._ 

import play.api.libs.json._ 
import play.api.libs.functional.syntax._ 

implicit val userReads: Reads[User] = (
    (__ \ 'id).readNullable[Int] and 
    (__ \ 'name).readNullable[String] and 
    (__ \ 'gender).readNullable[String] 
)((id, name, gender) => new User(
    id = if (id.isEmpty) default else id.get, 
    name = if (name.isEmpty) default else name.get, 
    gender = if (gender.isEmpty) default else gender.get 
)) 

Und schließlich:

scala> Json.fromJson[User](Json.parse("""{ "id": 1, "name": "Foo McBar" }""")) 
res0: play.api.libs.json.JsResult[User] = JsSuccess(User(1,Foo McBar,M),) 

scala> Json.fromJson[User](Json.parse("""{ "id": 2, "gender": "X" }""")) 
res1: play.api.libs.json.JsResult[User] = JsSuccess(User(2,John Doe,X),) 

Bitte beachte, dass ich nicht getOrElse bin mit, weil das Makro nicht unterstützt, aber es könnte leicht mehr allgemein- gemacht werden Es war nur ein kurzer Proof-of-Concept.

+2

Kein Kommentar zu der Idee von '" M "' als Standard Geschlecht übrigens. –

+0

Es wäre schön, wenn Makro könnte ganze lesen nicht nur einzelne Standardwerte generieren, aber für jetzt ist es die beste Lösung. – codez

+0

@codez: Sie könnten ein Makro schreiben, das würde (das ist, was Play Inception tut, obwohl es diese Funktionalität nicht bietet). Das Schöne an diesem Ansatz ist, dass es viel allgemeiner ist - Sie können 'default' auf diese Weise mit jeder JSON-Bibliothek verwenden. –

Verwandte Themen