2013-09-01 20 views
5

Ho qualche codice jackson scala module funzionante per arrotondare le classi di case scala. Jackson ha lavorato alla grande per classi di case piatte, ma quando ne ho realizzato uno che contiene un elenco di altre classi di casi la quantità di codice che mi sembrava avesse bisogno era molto. Considerare:Serializzazione json personalizzata di classi di case strutturate 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 

Per ottenere risultati cardset di andata e ritorno da/per JSON con modulo scala Jackson Ho usato un serializzatore personalizzato/deserializzatore scritto in Java:

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

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

e deserializzatore di corrispondenza:

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

} 

Questo sembra un codice molto da trattare con qualcosa di abbastanza vanigliato nella scala. Questo codice può essere migliorato (cosa mi è mancato che Jackson abbia reso questo facile)? Altrimenti c'è una libreria che farà automaticamente classi di case strutturate? Gli esempi di jerkson sembravano facili ma sembra essere stato abbandonato.

+0

ho provato martinetti che sembrava promettente, ma avuto un problema con queste classi, che ho riportato qui https://github.com/ wg/jacks/issues/15 – simbo1905

+0

Argonaut fa il lavoro con solo '' 'implicito lazy val CodecCardSet: CodecJson [CardSet] = casecodec2 (CardSet.apply, CardSet.unapply) (" cards "," mType ")' '' e '' 'implicit lazy val CodecCardDrawn: CodecJson [Card Drawn] = casecodec3 (CardDrawn.apply, CardDrawn.unapply) ("player", "card", "mType") '' 'guarda l'esempio su https://github.com/argonaut-io/argonaut/issues/64 – simbo1905

+0

hai preso in considerazione l'utilizzo del modulo Scala Jackson? https://github.com/FasterXML/jackson-module-scala –

risposta

0

Argonaut fa un ottimo lavoro. Mark Hibbard mi ha aiutato con l'esempio seguente di lavoro. Tutto ciò che serve è creare un codec per i tipi e aggiungerà implicitamente uno asJson agli oggetti per trasformarli in stringhe. Aggiungerà anche un decodeOption[YourClass] alle stringhe per estrarre un oggetto. Il seguente:

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

uscite:

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)

Problemi correlati