结构化scala案例类的自定义json序列化 [英] Custom json serialization of structured scala case classes

查看:129
本文介绍了结构化scala案例类的自定义json序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些工作的jackson scala模块代码用于往返scala案例类。杰克逊在平面案例课程中工作得非常好,但是当我创建一个包含其他案例类别的列表时,我似乎需要的代码量很多。考虑:

I have some working jackson scala module code for roundtripping scala case classes. Jackson worked great for flat case classes but when I made one which contains a list of other case classes the amount of code I seemed to need was a lot. Consider:

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

为了让CardSet与jackson scala模块一起往返于json,我使用了一个用java编写的自定义序列化器/解串器:

To get the CardSet to roundtrip to/from json with jackson scala module I used a custom serializer/deserializer written 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
    }
  }

包含序列化程序:

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

和匹配的反序列化器:

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

}

这似乎是很多代码处理scala中相当香草的东西。这段代码可以改进吗(我想念杰克逊必须做些什么)?还有一个库会自动进行结构化的案例类吗? jerkson的例子看起来很简单,但似乎已经放弃了。

This seems like a lot code to deal with something fairly vanilla in the scala. Can this code be improved (what did I miss that jackson has to make this easy)? Else is there a library which will do structured case classes automatically? The jerkson examples looked easy but that seems to have been abandoned.

推荐答案

Argonaut做得很好。 Mark Hibbard 帮我解决了下面的例子。所需要的只是为类型创建一个编解码器,它将隐式地向对象添加 asJson 以将它们转换为字符串。它还会向字符串添加 decodeOption [YourClass] 以提取对象。以下内容:

Argonaut does a great job. Mark Hibbard helped me out with getting the example below working. All that is needed is to create a codec for the types and it will implicitly add an asJson to your objects to turn them into strings. It will also add a decodeOption[YourClass] to strings to extract an object. The following:

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

输出:


得到一些好的json {牌:[{player:1,card:2,mType :CardDrawn},{player:3,card:4,mType:CardDrawn}],mType:CardSet}


有一个好的对象CardSet(ListDrawn(1,2,CardDrawn),CardDrawn(3,4,CardDrawn) ),CardSet)

这篇关于结构化scala案例类的自定义json序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆