2013-04-10 13 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) 

} 

Druga serializacja.wytrzymuje wyjątek: net.liftweb.json.MappingException: Brak użytecznej wartości dla nazwy.Jak wypełnić klasę case od json z częściowymi danymi?

Jak mogę odczytać dane z formularza json do klasy case, ale jeśli brakuje niektórych pól, zostaną one zastąpione wartościami domyślnymi z klasy case?

+0

Odpowiedź na pytanie, jak to zrobić z biblioteką odtwarzania json jest również do przyjęcia. – codez

Odpowiedz

5

Wygląda na to, że nastąpiła otwarta biletów na to od dłuższego czasu: https://www.assembla.com/spaces/liftweb/tickets/534

W międzyczasie jedna z opcji jest użycie Option:

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) 

To powinno dać wam:

User(1,None,None) 

Można użyć czegoś takiego, aby wypełnić domyślne wartości:

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

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

Inną opcją jest użycie dodatkowych konstruktorów dla swojej klasy obudowy:

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

Większość bibliotek scala JSON dusić w tej sprawie.

Używamy wewnętrznej biblioteki JSON, która używa makr, aby zapewnić prawidłowe domyślne brakujące pola. (rozróżnia również wartość Null jako none i Undefined jako wartość domyślną, więc możesz mieć opcję z domyślną wartością Some, jeśli chcesz)

Mamy nadzieję, że wkrótce zostanie otwarty.

EDYCJA: w zasadzie, nie znam żadnych innych, które to robią. ale jest to ciągle zmieniający się ekosystem, ciekawy, czy ktokolwiek jeszcze sobie z nim poradził:

5

Jak to zrobić z biblioteką odtwarzania json, chociaż musisz podać wartości domyślne w parserze, a nie tylko jako domyślne dla wartości:

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}""")) 

Chyba można dostarczyć domyślnych z parametrem domyślnym tworząc instancję szablonu użytkownika, a następnie przechodząc domyślne z każdego pola do Reads.pure zamiast sztywno tam ciąg.

4

Oto rozwiązanie Play, które nie wymaga dwukrotnego określania wartości domyślnych lub w jakimś dziwnym miejscu - używa makra do znalezienia odpowiedniego ustawienia domyślnego podczas kompilacji.

pierwszy dla klasy obudowy:

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

Następnie trzeba zdefiniować DefaultFinder jak opiszę w this blog post. Wtedy jesteś praktycznie zrobione:

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 
)) 

i wreszcie:

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),) 

Zauważ, że nie używam getOrElse ponieważ makro nie obsługuje, ale może z łatwością być bardziej General- to był tylko szybki dowód koncepcji.

+2

Brak komentarza na temat "M" jako domyślnej płci, przy okazji. –

+0

Byłoby miło, gdyby makro mogło generować całość, czytaj nie tylko pojedyncze wartości domyślne, ale na razie jest to najlepsze rozwiązanie. – codez

+0

@codez: możesz napisać makro, które mogłoby (to właśnie robi Incepcja gry, chociaż nie zapewnia tej funkcji). Zaletą tego podejścia jest to, że jest on bardziej ogólny - możesz użyć 'default' w ten sposób z dowolną biblioteką JSON. –

Powiązane problemy