2011-09-23 9 views
6

Sto tentando di deserializzare automaticamente l'oggetto json in una classe di scala utilizzando Lift-Json con una classe di coordinate all'interno utilizzata per memorizzare le informazioni di GeoJson.Deserializzazione elevatore-json polimerico in una classe composta

case class Request(name:String, geometry:Geometry) 

sealed abstract class Geometry 

case class Point(coordinates:(Double,Double)) extends Geometry 
case class LineString(coordinates:List[Point]) extends Geometry 
case class Polygon(coordinates:List[LineString]) extends Geometry 

voglio deserializzare una stringa JSON come questo:

{ 
name:"test", 
geometry:{ 
    "type": "LineString", 
    "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] 
    } 
} 

in una classe Richiesta caso del diritto di classe LineString runtime nel campo della geometria. Suppongo che dovrei usare un TypeHint ma come? È questo l'approccio corretto o dovrei creare tre diverse richieste (RequestPoint, RequestLineString e RequestPolygon)? Questo sarebbe il codice Scala per deserializzare:

val json = parse(message) 
json.extract[Request] 

risposta

6

Sì, è necessario utilizzare tipo suggerimenti per i tipi di somma come la geometria. Ecco un esempio:

implicit val formats = DefaultFormats.withHints(ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon]))) 

val r = Request("test", LineString(List(Point(100.0, 0.0), Point(101.0, 1.0)))) 

Serialization.write(r) 

{ 
"name":"test", 
"geometry":{ 
    "jsonClass":"LineString", 
    "coordinates":[{"jsonClass":"Point","coordinates":{"_1$mcD$sp":100.0,"_2$mcD$sp":0.0}},{"jsonClass":"Point","coordinates":{"_1$mcD$sp":101.0,"_2$mcD$sp":1.0}}]} 
} 

Non proprio quello che volevi. Poiché si desidera modificare lo schema di serializzazione predefinito per Punti, è necessario fornire un serializzatore personalizzato per quel tipo.

class PointSerializer extends Serializer[Point] { 
    private val Class = classOf[Point] 

    def deserialize(implicit format: Formats) = { 
    case (TypeInfo(Class, _), json) => json match { 
     case JArray(JDouble(x) :: JDouble(y) :: Nil) => Point(x, y) 
     case x => throw new MappingException("Can't convert " + x + " to Point") 
    } 
    } 

    def serialize(implicit format: Formats) = { 
    case p: Point => JArray(JDouble(p.coordinates._1) :: JDouble(p.coordinates._2) :: Nil) 
    } 
} 

// Configure it 
implicit val formats = DefaultFormats.withHints(ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon]))) + new PointSerializer 

Serialization.write(r) 

{ 
    "name":"test", 
    "geometry":{ 
    "jsonClass":"LineString", 
    "coordinates":[[100.0,0.0],[101.0,1.0]] 
    } 
} 

meglio, ma è necessario una configurazione di più se è necessario modificare il campo di default denominato come 'jsonClass' a 'Tipo':

implicit val formats = new DefaultFormats { 
    override val typeHintFieldName = "type" 
    override val typeHints = ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon])) 
} + new PointSerializer 

Serialization.write(r) 

{ 
    "name":"test", 
    "geometry":{ 
    "type":"LineString", 
    "coordinates":[[100.0,0.0],[101.0,1.0]] 
    } 
} 
Problemi correlati