2013-09-01 36 views
5

Tôi có một số mã mô-đun scala jackson làm việc để roundtripping các lớp scala case. Jackson làm việc tuyệt vời cho các trường hợp trường hợp bằng phẳng nhưng khi tôi thực hiện một trong đó có một danh sách các trường hợp các lớp khác số lượng mã tôi dường như cần là rất nhiều. Xem xét:Tùy chỉnh tuần tự json của các lớp scala case có cấu trúc

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 

Để có được CardSet để khứ hồi đến/từ json với module scala jackson tôi đã sử dụng một serializer tùy chỉnh/deserializer viết bằng 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 
    } 
    } 

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

và khớp với 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"); 
    } 

} 

Điều này có vẻ như rất nhiều mã để đối phó với một cái gì đó khá vani trong scala. Mã này có thể được cải thiện không (tôi đã bỏ lỡ điều gì mà jackson phải làm cho việc này trở nên dễ dàng)? Khác là có một thư viện mà sẽ làm các lớp trường hợp có cấu trúc tự động? Các ví dụ jerkson trông dễ dàng nhưng dường như đã bị bỏ rơi.

+0

tôi đã cố gắng giắc trông đầy hứa hẹn nhưng có một vấn đề với các lớp mà tôi trình bày ở đây https://github.com/ wg/jacks/issues/15 – simbo1905

+0

Argonaut thực hiện công việc chỉ với '' 'CodecCardSet ngầm: CodecJson [CardSet] = casecodec2 (CardSet.apply, CardSet.unapply) (" cards "," mType ")' '' và '' 'CodecCardDrawn lười biếng ẩn: CodecJson [Thẻ Drawn] = casecodec3 (CardDrawn.apply, CardDrawn.unapply) ("trình phát", "thẻ", "mType") '' 'xem ví dụ tại https://github.com/argonaut-io/argonaut/issues/64 – simbo1905

+0

bạn đã cân nhắc sử dụng Mô-đun Scala Jackson chưa? https://github.com/FasterXML/jackson-module-scala –

Trả lời

0

Argonaut thực hiện công việc tuyệt vời. Mark Hibbard đã giúp tôi với ví dụ bên dưới hoạt động. Tất cả những gì cần thiết là tạo một codec cho các loại và nó sẽ ngầm thêm một asJson vào các đối tượng của bạn để biến chúng thành chuỗi. Nó cũng sẽ thêm một decodeOption[YourClass] vào các chuỗi để trích xuất một đối tượng. Sau đây:

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

kết quả đầu ra:

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)

Các vấn đề liên quan