2013-09-01 11 views
5

Ich habe einige Arbeits Jackson Scala-Modul-Code zum Roundtripping Scala Fall Klassen. Jackson arbeitete großartig für flache Fall-Klassen, aber als ich eine erstellte, die eine Liste von anderen Fall-Klassen enthält, war die Menge an Code, den ich zu brauchen schien, eine Menge. Bedenken Sie:Benutzerdefinierte Json-Serialisierung von strukturierten Scala-Fallklassen

abstract class Message 
case class CardDrawn(player: Long, card: Int, mType: String = "CardDrawn") extends Message 
case class CardSet(cards: List[CardDrawn], mType: String = "CardSet") extends Message 

Um die Cardset zu bekommen Roundtrip zu/von json mit jackson scala Modul ich einen benutzerdefinierten Serializer verwendet/in Java geschrieben Deserializer:

object ScrumGameMashaller { 

    val mapper = new ObjectMapper() 
    val module = new SimpleModule("CustomSerializer") 
    module.addSerializer(classOf[CardSet], new CardSetSerializer) 
    module.addDeserializer(classOf[CardSet], new CardSetDeserializer) 
    val scalaModule = DefaultScalaModule 
    mapper.registerModule(scalaModule) 
    mapper.registerModule(module) 

    def jsonFrom(value: Any): String = { 
    import java.io.StringWriter 
    val writer = new StringWriter() 
    mapper.writeValue(writer, value) 
    writer.toString 
    } 

    private[this] def objectFrom[T: Manifest](value: String): T = 
    mapper.readValue(value, typeReference[T]) 

    private[this] def typeReference[T: Manifest] = new TypeReference[T] { 
    override def getType = typeFromManifest(manifest[T]) 
    } 

    private[this] def typeFromManifest(m: Manifest[_]): Type = { 
    if (m.typeArguments.isEmpty) { m.runtimeClass } 
    else new ParameterizedType { 
     def getRawType = m.runtimeClass 
     def getActualTypeArguments = m.typeArguments.map(typeFromManifest).toArray 
     def getOwnerType = null 
    } 
    } 

mit Serializer:

public class CardSetSerializer extends JsonSerializer<CardSet> { 
@Override 
    public void serialize(CardSet cardSet, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { 
     jgen.writeStartObject(); 
     jgen.writeArrayFieldStart("cards"); 
     List<CardDrawn> cardsDrawn = cardSet.cards(); 
     scala.collection.Iterator<CardDrawn> iter = cardsDrawn.iterator(); 
     while(iter.hasNext()){ 
      CardDrawn cd = iter.next(); 
      cdSerialize(jgen,cd); 
     } 
     jgen.writeEndArray(); 
     jgen.writeStringField("mType", "CardSet"); 
     jgen.writeEndObject();  
    } 

    private void cdSerialize(JsonGenerator jgen, CardDrawn cd) throws IOException, JsonProcessingException { 
     jgen.writeStartObject(); 
     jgen.writeNumberField("player", cd.player()); 
     jgen.writeNumberField("card", cd.card()); 
     jgen.writeEndObject(); 
    } 
} 

und passender Deserializer:

public class CardSetDeserializer extends JsonDeserializer<CardSet> { 

    private static class CardDrawnTuple { 
     Long player; 
     Integer card; 
    } 

    @Override 
    public CardSet deserialize(JsonParser jsonParser, DeserializationContext cxt) throws IOException, JsonProcessingException { 
     ObjectCodec oc = jsonParser.getCodec(); 
     JsonNode root = oc.readTree(jsonParser); 
     JsonNode cards = root.get("cards"); 
     Iterator<JsonNode> i = cards.elements(); 
     List<CardDrawn> cardObjects = new ArrayList<>(); 
     while(i.hasNext()){ 
      CardDrawnTuple t = new CardDrawnTuple(); 
      ObjectNode c = (ObjectNode) i.next(); 
      Iterator<Entry<String, JsonNode>> fields = c.fields(); 
      while(fields.hasNext()){ 
       Entry<String,JsonNode> f = fields.next(); 
       if(f.getKey().equals("player")) { 
        t.player = f.getValue().asLong(); 
       } else if(f.getKey().equals("card")){ 
        t.card = f.getValue().asInt(); 
       } else { 
        System.err.println(CardSetDeserializer.class.getCanonicalName()+ " : unknown field " + f.getKey()); 
       } 
      } 
      CardDrawn cd = new CardDrawn(t.player, t.card, "CardDrawn"); 
      cardObjects.add(cd); 
     } 

     return new CardSet(JavaConversions.asScalaBuffer(cardObjects).toList(), "CardSet"); 
    } 

} 

Das scheint eine Menge Code zu sein, mit etwas ziemlich Vanille in der Scala umzugehen. Kann dieser Code verbessert werden (was habe ich vermisst, dass Jackson das so einfach machen muss)? Gibt es eine Bibliothek, die automatisch strukturierte Fallklassen durchführt? Die Jerkson-Beispiele sahen einfach aus, aber das scheint aufgegeben worden zu sein.

+0

I-Buchsen versucht, die vielversprechend aussah, hatte aber ein Problem mit diesen Klassen, die ich hier berichten https://github.com/ wg/jacks/issues/15 – simbo1905

+0

Argonaut macht den Job mit nur '' 'implizit lazy val CodecCardSet: CodecJson [CardSet] = casecodec2 (CardSet.apply, CardSet.unapply) (" Karten "," mType ")' '' und '' 'implizite lazy val CodecCardDrawn: CodecJson [Karte Gezeichnet] = casecodec3 (CardDrawn.apply, CardDrawn.unapply) ("player", "card", "mType") '' 'siehe Beispiel unter https://github.com/argonaut-io/argonaut/issues/64 – simbo1905

+0

Haben Sie überlegt, das Scala Jackson Modul zu benutzen? https://github.com/FasterXML/jackson-module-scala –

Antwort

0

Argonaut macht einen tollen Job. Mark Hibbard hat mir geholfen, das folgende Beispiel zu bekommen. Sie müssen lediglich einen Codec für die Typen erstellen und fügen Ihren Objekten implizit asJson hinzu, um sie in Zeichenfolgen umzuwandeln. Es wird auch eine decodeOption[YourClass] zu Strings hinzufügen, um ein Objekt zu extrahieren. In den folgenden:

package argonaut.example 

import argonaut._, Argonaut._ 

abstract class Message 
case class CardDrawn(player: Long, card: Int, mType: String = "CardDrawn") extends Message 
case class CardSet(cards: List[CardDrawn], mType: String = "CardSet") extends Message 

object CardSetExample { 

    implicit lazy val CodecCardSet: CodecJson[CardSet] = casecodec2(CardSet.apply, CardSet.unapply)("cards","mType") 
    implicit lazy val CodecCardDrawn: CodecJson[CardDrawn] = casecodec3(CardDrawn.apply, CardDrawn.unapply)("player", "card", "mType") 

    def main(args: Array[String]): Unit = { 
    val value = CardSet(List(CardDrawn(1L,2),CardDrawn(3L,4))) 
    println(s"Got some good json ${value.asJson}") 

    val jstring = 
     """{ 
     | "cards":[ 
     | {"player":"1","card":2,"mType":"CardDrawn"}, 
     | {"player":"3","card":4,"mType":"CardDrawn"} 
     | ], 
     | "mType":"CardSet" 
     | }""".stripMargin 

    val parsed: Option[CardSet] = 
     jstring.decodeOption[CardSet] 

    println(s"Got a good object ${parsed.get}") 
    } 
} 

Ausgänge:

Got some good json {"cards":[{"player":"1","card":2,"mType":"CardDrawn"},{"player":"3","card":4,"mType":"CardDrawn"}],"mType":"CardSet"}

Got a good object CardSet(List(CardDrawn(1,2,CardDrawn), CardDrawn(3,4,CardDrawn)),CardSet)

Verwandte Themen