Jackson:アノテーション無しのカスタムシリアライゼーション・デシリアライゼーション
Jacksonにおいて、カスタムなシリアライゼーション・デシリアライゼーションを行いたいことがある。例えば、以下のオブジェクトについて、
class Foo {
  int a;
  int b;
}
...
Foo foo = new Foo();
foo.a = 123;
foo.b = 456;
通常であれば、以下のようにシリアライズされる。
{"a":123,"b":456}
しかし時には、普通でないシリアライゼーション・デシリアライゼーションを行いたい。例えば、
"123:456"
という単一の文字列としてシリアライゼーション・デシリアライゼーションしたいものとする。
しかし、巷の例では「FooクラスにアノテーションをつけてJacksonに指示する」という解決法がほとんどだ。しかし以下のような状況もある。
- アノテーションをつけたくない。
 - アノテーションをつけられない。
 
この場合にどうするかを示したのが以下である。もちろんこれはあくまでも例だ。
ちなみにObjectMapperのコンフィギュレーションについては、Jackson:ObjectMapperのコンフィギュレーションを参照のこと。
// gradleの依存
dependencies {
  compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9.1' 
  testCompile group: 'junit', name: 'junit', version: '4.12'
}
import static org.junit.Assert.*;
import java.io.*;
import java.lang.reflect.*;
import org.junit.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect.*;
import com.fasterxml.jackson.annotation.JsonInclude.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.*;
import com.fasterxml.jackson.databind.module.*;
import com.fasterxml.jackson.databind.node.*;
import com.fasterxml.jackson.databind.ser.std.*;
public class JacksonSample {
  @Test
  public void test() throws Exception {
    Foo foo = new Foo(123, 456);
    String json = mapper.writeValueAsString(foo);
    assertEquals("\"123:456\"",  json);
    Foo deserialized = mapper.readValue(json, Foo.class);    
    assertEquals(123, deserialized.a);
    assertEquals(456, deserialized.b);
  }
  static class Foo {
    int a;
    int b;
    public Foo(String string) {
      String[]splited = string.split(":");      
      a = Integer.parseInt(splited[0]);
      b = Integer.parseInt(splited[1]);
    }
    public Foo(int a, int b) {
      this.a = a;
      this.b = b;
    }
    @Override
    public String toString() {
      return a + ":" + b;
    }
  }
  SimpleModule module = new SimpleModule()
      .addSerializer(new ToStringSerializer<Foo>(Foo.class))
      .addDeserializer(Foo.class, new ConstructorDeserializer<Foo>(Foo.class))
      ;
  ObjectMapper mapper = new ObjectMapper()
      .setVisibility(PropertyAccessor.ALL, Visibility.NONE)
      .setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
      .setSerializationInclusion(Include.NON_NULL)
      .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
      .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
      .registerModule(module);
  ;      
  static class ConstructorDeserializer<T> extends StdDeserializer<T> {
    /** 単一の文字列を受け入れるコンストラクタ */
    final Constructor<T>ctr;
    /**
     * クラス指定は必須。
     * @param vc 対象クラス
     */
    public ConstructorDeserializer(Class<T> vc) { 
      super(vc); 
      try {
        ctr = vc.getConstructor(String.class);
      } catch (Exception ex) {
        throw new RuntimeException(ex);
      }
    }
    /**
     * デシリアライズする
     */
    @Override
    public T deserialize(JsonParser jp, DeserializationContext ctxt) 
      throws IOException, JsonProcessingException {    
      TextNode node = (TextNode)jp.getCodec().readTree(jp);  
      try {
        return ctr.newInstance(node.asText());
      } catch (Exception ex) {
        throw new IOException(ex);
      }
    }
  }
  static class ToStringSerializer<T> extends StdSerializer<T> {    
    public ToStringSerializer() {
      this(null);
    }   
    public ToStringSerializer(Class<T> t) {
      super(t);
    }
    /** 値オブジェクトのtoString()を呼び出してJSON値とする。 */
    @Override
    public void serialize(
      T value, JsonGenerator jgen, SerializerProvider provider) 
      throws IOException, JsonProcessingException {
      jgen.writeString(value.toString());
    }
  }
}




ディスカッション
コメント一覧
まだ、コメントがありません