2012-12-18 9 views
6

Piszę aplikację Scala, która musi serializować i deserializować z JSON. Niektóre obiekty JSON mają więcej niż 22 pola, więc nie mogę używać klas przypadków (i nie mogę zmienić formatu). Wszystkie biblioteki Scala JSON, które udało mi się znaleźć, działają (łatwo) tylko z klasami przypadków, a nie z normalnymi klasami.Jak można deserializować z JSON ze Scala za pomocą * nie-przypadku * klas?

Biorąc to pod uwagę, jaki jest najprostszy sposób na deserializację dużego obiektu JSON (z więcej niż 22 polami) w klasie Scala bez klauzuli? Nie musi to być całkowicie automatyczne, ale najlepiej szukam czegoś mniej bolesnego niż deserializacja do Map [String, Any] i ręczne robienie wszystkiego.

Odpowiedz

4

Aktualizacja: Na szczęście, jest to obecnie możliwe, aby zrobić to, co chciałem za pomocą Json4s i Jackson za pomocą serializatora pola w następujący sposób:

implicit val formats = DefaultFormats + FieldSerializer[MyNonCaseClass]() 

val myNonCaseClassObject = Serialization.read[MyNonCaseClass](jsonString) 

Zgodnie z wnioskiem poniżej oto bardziej kompletny przykład:

import org.json4s.jackson.Serialization 
import org.json4s._ 
import scala.util.Try 

object JSONUtil { 

implicit val formats = DefaultFormats + FieldSerializer[MyNonCaseClass]() + FieldSerializer[MyOtherNonCaseClass](ignore("someUnwantedFieldName") orElse ignore("anotherFieldToIgnore")) + ... 

def toJSON(objectToWrite: AnyRef): String = Serialization.write(objectToWrite) 

def fromJSONOption[T](jsonString: String)(implicit mf: Manifest[T]): Option[T] = Try(Serialization.read(jsonString)).toOption 

} 

Następnie użycie jest:

val jsonString = JSONUtil.toJSON(myObject) 
val myNewObject: Option[MyClass] = JSONUtil.fromJSONOption[MyClass](aJsonString) 

Musisz FieldSerializer dla każdego nie-CA se klasy, którą chcesz serializować. Ponadto podczas definiowania klas, wszystko, czego może brakować w JSON, należy zdefiniować jako opcję.

SBT:

"org.json4s" %% "json4s-jackson" % "3.2.6" 
+0

Cześć Ryan, czy mógłbyś bardziej szczegółowo opisać sposób, w jaki wszystko konfigurujesz? Jestem na to nowy i przynajmniej widzę, że import byłby naprawdę pomocny! (Identyfikatory repozytorium SBT/Maven również są mile widziane!) – fortran

+1

Dodałem pełniejszy przykład do powyższej odpowiedzi. – Ryan

+0

Dzięki Ryan! Mimo że Scala jest językiem silnie statycznym i statycznym, czasami wydaje mi się, że nie można tego wykorzystać do "odkrywania" za pomocą autouzupełniania w IDE tak samo jak w Javie (głównie z implikacji); i to sprawia, że ​​czuję się trochę zagubiony ... – fortran

3

Jest możliwe, aby to zrobić bez klas przypadku użycia The Play JSON library with generics

Jak piłem kawę i nic nie robi. Zrobiłem to, aby podać dla ciebie przykład. Kompletne rozwiązanie jest następujące:

Przede wszystkim, jest to klasa:

import play.api.libs.json._ 
import play.api.libs.json.Json._ 


    class TestJSON(
     val field1: String, 
     val field2: String, 
     val field3: String, 
     val field4: String, 
     val field5: String, 
     val field6: String, 
     val field7: String, 
     val field8: String, 
     val field9: String, 
     val field10: String, 
     val field11: String, 
     val field12: String, 
     val field13: String, 
     val field14: String, 
     val field15: String, 
     val field16: String, 
     val field17: String, 
     val field18: String, 
     val field19: String, 
     val field20: String, 
     val field21: String, 
     val field22: String, 
     val field23: String) { 

    } 

    object TestJSON { 
     // 
     // JSON BINDING/UNBINDING 
     // 

     implicit def modalityReads: Reads[TestJSON] = new Reads[TestJSON] { 
     def reads(json: JsValue): TestJSON = 
      new TestJSON(
      field1 = (json \ "field1").as[String], 
      field2 = (json \ "field2").as[String], 
      field3 = (json \ "field3").as[String], 
      field4 = (json \ "field4").as[String], 
      field5 = (json \ "field5").as[String], 
      field6 = (json \ "field6").as[String], 
      field7 = (json \ "field7").as[String], 
      field8 = (json \ "field8").as[String], 
      field9 = (json \ "field9").as[String], 
      field10 = (json \ "field10").as[String], 
      field11 = (json \ "field11").as[String], 
      field12 = (json \ "field12").as[String], 
      field13 = (json \ "field13").as[String], 
      field14 = (json \ "field14").as[String], 
      field15 = (json \ "field15").as[String], 
      field16 = (json \ "field16").as[String], 
      field17 = (json \ "field17").as[String], 
      field18 = (json \ "field18").as[String], 
      field19 = (json \ "field19").as[String], 
      field20 = (json \ "field20").as[String], 
      field21 = (json \ "field21").as[String], 
      field22 = (json \ "field22").as[String], 
      field23 = (json \ "field22").as[String]) 
     } 

     implicit def modalityWrites: Writes[TestJSON] = new Writes[TestJSON] { 
     def writes(ts: TestJSON) = JsObject(Seq(
      "field1" -> JsString(ts.field1), 
      "field2" -> JsString(ts.field2), 
      "field3" -> JsString(ts.field3), 
      "field4" -> JsString(ts.field4), 
      "field5" -> JsString(ts.field5), 
      "field6" -> JsString(ts.field6), 
      "field7" -> JsString(ts.field7), 
      "field8" -> JsString(ts.field8), 
      "field9" -> JsString(ts.field9), 
      "field10" -> JsString(ts.field10), 
      "field11" -> JsString(ts.field11), 
      "field12" -> JsString(ts.field12), 
      "field13" -> JsString(ts.field13), 
      "field14" -> JsString(ts.field14), 
      "field15" -> JsString(ts.field15), 
      "field16" -> JsString(ts.field16), 
      "field17" -> JsString(ts.field17), 
      "field18" -> JsString(ts.field18), 
      "field19" -> JsString(ts.field19), 
      "field20" -> JsString(ts.field20), 
      "field21" -> JsString(ts.field21), 
      "field22" -> JsString(ts.field22), 
      "field23" -> JsString(ts.field23))) 
     } 
    } 

Kontroler powinien wyglądać następująco:

import play.api._ 
import play.api.mvc._ 
import play.api.libs.json.Json._ 
import play.api.Play.current 
import models.TestJSON 

object Application extends Controller { 

    def getJson = Action { 
    implicit request => 
     Ok(
     toJson(
      Seq(
      toJson(
       new TestJSON(
       "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", 
       "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23")), 
       toJson(new TestJSON(
       "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", 
       "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"))))) 
    } 

} 

Plik trasy (tylko trasa działanie) :

GET  /getJson        controllers.Application.getJson 

A teraz chwila prawdy ...

curl localhost:9000/getJson 
[{"field1":"1","field2":"2","field3":"3","field4":"4","field5":"5","field6":"6", 
"field7":"7","field8":"8","field9":"9","field10":"10","field11":"11","field12":" 
12","field13":"13","field14":"14","field15":"15","field16":"16","field17":"17"," 
field18":"18","field19":"19","field20":"20","field21":"21","field22":"22","field 
23":"23"},{"field1":"1","field2":"2","field3":"3","field4":"4","field5":"5","fie 
ld6":"6","field7":"7","field8":"8","field9":"9","field10":"10","field11":"11","f 
ield12":"12","field13":"13","field14":"14","field15":"15","field16":"16","field1 
7":"17","field18":"18","field19":"19","field20":"20","field21":"21","field22":"2 
2","field23":"23"}] 

Powinien również działać w drugą stronę. Obecnie pracuję nad projektem, który wykorzystuje to do montażu i demontażu ogromnych drzew, więc powinien on działać dla ciebie. Daj mi znać.

Pozdrawiam!

PS: Nie martw się, wygenerowanie kodu zajęło mi około 10 minut. Po prostu zmapowałem List.range (1,24) i "przepchnąłem" go, aby wydrukować kod.

+1

Dziękuję za wysiłek, ale to, co starałem się uniknąć (np konieczności ręcznego obsłużyć każdego pola). Zamiast lepszej opcji robię to na razie używając Jerksona - przynajmniej wszystkie moje sub-obiekty są mniejsze niż 22 pola, więc mogę deserializować te normalnie. – Ryan

+1

Jeśli definiujesz instancje 'Reads' i' Write', równie dobrze możesz użyć 'Format'. –

+0

@Travis, yeap, używam go w moim kodzie produkcyjnym. Twoje zdrowie! – wleao

1

Zapoznaj się z Lift JSON API.

Kluczem jest to, że wszystko, co zostanie przeanalizowane, powraca jako podklasa JValue. Obiekt jak

{ 
    "a": [1, 2], 
    "b": "hello" 
} 

zostanie sparsowany

JObject(List(
    JField("a", JArray(List(JInt(1), JInt(2)))), 
    JField("b", JString("hello")) 
)) 

API Winda zapewnia kilka przydatnych metod, takich jak \ które umożliwiają dostęp do rzeczy, jak Map. Istnieje również metoda extractOpt[A], która postara się jak najlepiej przekształcić przeanalizowany plik JSON w dowolne wymagane przez ciebie A. Pobaw się z nimi, aby poczuć to.

+0

można również uzyskać "prymitywne" mapowanie JSON z bibliotekami odtwarzania, chodzi o to, aby uzyskać ładną klasę modeli z jej metodami i innymi niuansami. – fortran

Powiązane problemy