2013-09-01 15 views
5

Mam kilka działającego kodu modułu scala jackson dla klas klasycznych zajęć klasycznych. Jackson świetnie pasował do klas płaskich, ale kiedy zrobiłem taki, który zawiera listę innych klas przypadków, ilość kodu, którego potrzebowałem, była bardzo duża. Rozważmy:Niestandardowa serializacja json klas uporządkowanych klas scala

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 

Aby uzyskać cardset aby obie strony do/z json z modułem scala jackson użyłem niestandardowe serializatora/Deserializator napisany w Javie:

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

z serializatora:

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(); 
    } 
} 

i pasujący deserializator:

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

} 

To wydaje się dużo kodu, aby poradzić sobie z czymś dość wanilii w scala. Czy ten kod może być ulepszony (czego brakowało mi, żeby Jackson miał to ułatwić)? Czy istnieje biblioteka, która automatycznie tworzy uporządkowane klasy spraw? Przykłady jerksona wydawały się łatwe, ale wydaje się, że zostały porzucone.

+0

Próbowałem gniazda, które wyglądały obiecująco, ale miał problem z tych klas, które zgłosiłem tutaj https://github.com/ wg/jacks/issues/15 – simbo1905

+0

Argonaut wykonuje zadanie za pomocą '' 'implicit lazy val CodecCardSet: CodecJson [CardSet] = casecodec2 (CardSet.apply, CardSet.unapply) (" cards "," mType ")' '' i '' 'niejawny lazy val CodecCardDrawn: CodecJson [Card Drawn] = casecodec3 (CardDrawn.apply, CardDrawn.unapply) ("player", "card", "mType") '' 'zobacz przykład na https://github.com/argonaut-io/argonaut/issues/64 – simbo1905

+0

czy rozważałeś użycie modułu Scala Jackson? https://github.com/FasterXML/jackson-module-scala –

Odpowiedz

0

Argonaut wykonuje świetną robotę. Mark Hibbard pomógł mi w uzyskaniu przykładu poniżej. Wszystko, czego potrzeba, to stworzenie kodeków dla typów i domyślnie doda on do twoich obiektów asJson, aby zamienić je w łańcuchy. Doda także decodeOption[YourClass] do ciągów znaków w celu wyodrębnienia obiektu. Dodaje:

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

wyjścia:

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)

Powiązane problemy