2013-08-26 18 views
16

Często muszę serializować/deserializować typy sum (jak Either[S,T]) i nie znalazłem jeszcze ogólnego lub eleganckiego sposobu, aby to zrobić. Oto przykład typ (zasadniczo równoważne Either)Serializacja Json dla cechy z wieloma klasami przypadków (typy sum) w grze Scala

sealed trait OutcomeType 
case class NumericOutcome(units: String)    extends OutcomeType 
case class QualitativeOutcome(outcomes: List[String]) extends OutcomeType 

Oto mój najlepszy wysiłek w obiekcie towarzysz, który implementuje serializacji. Działa, ale pisanie tego rodzaju rzeczy za każdym razem jest bardzo męczące dla każdego rodzaju sumy. Czy są jakieś sugestie, aby uczynić je ładniejszymi i/lub bardziej ogólnymi?

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

object OutcomeType { 

    val fmtNumeric  = Json.format[NumericOutcome] 
    val fmtQualitative = Json.format[QualitativeOutcome] 

    implicit object FormatOutcomeType extends Format[OutcomeType] { 
    def writes(o: OutcomeType) = o match { 
     case [email protected](_)  => Json.obj("NumericOutcome"  -> Json.toJson(n)(fmtNumeric)) 
     case [email protected](_) => Json.obj("QualitativeOutcome" -> Json.toJson(q)(fmtQualitative)) 
    } 

    def reads(json: JsValue) = (
     Json.fromJson(json \ "NumericOutcome")(fmtNumeric) orElse 
     Json.fromJson(json \ "QualitativeOutcome")(fmtQualitative) 
    ) 
    } 
} 
+0

Czy próbowałeś json4s http://json4s.org? Również jeśli chcesz go używać z grą, powinieneś zajrzeć tutaj: https://github.com/tototoshi/play-json4s lub zaimplementować to samemu. –

+0

Wygląda miło. Czy mógłbyś zaktualizować swój najlepszy wysiłek, by grać2.5? Dzięki! – qed

+0

Nieważne, dowiedziałem się, jak to zrobić w play2.5 i umieścić w odpowiedzi. – qed

Odpowiedz

0

myślę, że jest tak proste, jak można zrobić to, jeśli chcesz uniknąć pisania kodu dla każdej wyraźnej podtypu może można zrobić to z refleksji, użyj Jackson bezpośrednio lub jakiś inny json biblioteka z refleksji wsparcie. Lub wpisz własne makro, aby wygenerować format z listy podtypów.

0

Mam systematyczne rozwiązanie problemu serializacji typów sum w mojej bibliotece marynowania json Prickle. Podobne pomysły można zastosować w Play. Jest jeszcze jakiś kod config wymagane, ale jego wysoki sygnał/szum, np kod końcowy jak:

implicit val fruitPickler = CompositePickler[Fruit].concreteType[Apple].concreteType[Lemon]

CompositePicklers związany z supertypem są skonfigurowane z jednym PicklerPair dla każdego znanego podtypu (tj opcji typu suma). Powiązania są konfigurowane w czasie konfiguracji.

Podczas pickling deskryptor jest emitowany do strumienia json opisujący podtyp, który jest rekordem.

Podczas unpickling, deskryptor jest odczytywany z JSON, a następnie wykorzystywane do zlokalizowania odpowiedniego Unpickler dla podtypu

0

Przykładem zaktualizowanego do gry 2.5:

object TestContact extends App { 

    sealed trait Shape 

    object Shape { 
    val rectFormat = Json.format[Rect] 
    val circleFormat = Json.format[Circle] 

    implicit object ShapeFormat extends Format[Shape] { 
     override def writes(shape: Shape): JsValue = shape match { 
     case rect: Rect => 
      Json.obj("Shape" -> 
      Json.obj("Rect" -> 
       Json.toJson(rect)(rectFormat))) 
     case circle: Circle => 
      Json.obj("Shape" -> 
      Json.obj("Circle" -> 
       Json.toJson(circle)(circleFormat))) 
     } 

     override def reads(json: JsValue): JsResult[Shape] = { 
     json \ "Shape" \ "Rect" match { 
      case JsDefined(rectJson) => rectJson.validate[Rect](rectFormat) 
      case _ => json \ "Shape" \ "Circle" match { 
      case JsDefined(circleJson) => circleJson.validate[Circle](circleFormat) 
      case _ => JsError("Not a valide Shape object.") 
      } 
     } 
     } 
    } 

    } 

    case class Rect(width: Double, height: Double) extends Shape 

    case class Circle(radius: Double) extends Shape 

    val circle = Circle(2.1) 
    println(Json.toJson(circle)) 
    val rect = Rect(1.3, 8.9) 
    println(Json.toJson(rect)) 

    var json = Json.obj("Shape" -> Json.obj("Circle" -> Json.obj("radius" -> 4.13))) 
    println(json.validate[Shape]) 
    json = 
    Json.obj("Shape" -> 
     Json.obj("Rect" -> 
     Json.obj("width" -> 23.1, "height" -> 34.7))) 
    println(json.validate[Shape]) 
}