web-dev-qa-db-ja.com

ジャクソン:各値の正しい型でMap <String、Object>にデシリアライズします

次のようなクラスがあります

public class MyClass {
   private String val1;
   private String val2;
   private Map<String,Object> context;
   // Appropriate accessors removed for brevity.
   ...
}

私はジャクソンとオブジェクトからJSONへ、そしてその逆に往復できるようになりたいと思っています。上記のオブジェクトを細かくシリアル化し、次の出力を受け取ることができます。

{
    "val1": "foo",
    "val2": "bar",
    "context": {
        "key1": "enumValue1",
        "key2": "stringValue1",
        "key3": 3.0
    }
}

私が直面している問題は、シリアル化されたマップの値に型情報がないため、正しくシリアル化解除されないことです。たとえば、上記のサンプルでは、​​enumValue1は列挙値として逆シリアル化される必要がありますが、代わりに文字列として逆シリアル化されます。さまざまな事柄に基づいてどのタイプを使用するかという例を見てきましたが、私のシナリオでは、タイプが何なのかわかりません(これらは事前にわからないユーザー生成オブジェクトになります)タイプ情報をキーと値のペアでシリアル化できます。ジャクソンでこれを達成するにはどうすればよいですか?

記録のために、ジャクソンバージョン2.4.2を使用しています。往復のテストに使用しているコードは次のとおりです。

@Test
@SuppressWarnings("unchecked")
public void testJsonSerialization() throws Exception {
    // Get test object to serialize
    T serializationValue = getSerializationValue();
    // Serialize test object
    String json = mapper.writeValueAsString(serializationValue);
    // Test that object was serialized as expected
    assertJson(json);
    // Deserialize to complete round trip
    T roundTrip = (T) mapper.readValue(json, serializationValue.getClass());
    // Validate that the deserialized object matches the original one
    assertObject(roundTrip);
}

これはSpringベースのプロジェクトであるため、マッパーは次のように作成されています。

@Configuration
public static class SerializationConfiguration {

    @Bean
    public ObjectMapper mapper() {
        Map<Class<?>, Class<?>> mixins = new HashMap<Class<?>, Class<?>>();
        // Add unrelated MixIns
        .. 

        return new Jackson2ObjectMapperBuilder()
                .featuresToDisable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)
                .dateFormat(new ISO8601DateFormatWithMilliSeconds())
                .mixIns(mixins)
                .build();
    }
}
30
Michael Minella

あなたが望むものを達成する最も簡単な方法は、使用することだと思います:

ObjectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

これにより、シリアル化されたJSONに型情報が追加されます。

以下は実行例です。Springに適応する必要があります。

public class Main {

    public enum MyEnum {
        enumValue1
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();

        MyClass obj = new MyClass();
        obj.setContext(new HashMap<String, Object>());

        obj.setVal1("foo");
        obj.setVal2("var");
        obj.getContext().put("key1", "stringValue1");
        obj.getContext().put("key2", MyEnum.enumValue1);
        obj.getContext().put("key3", 3.0);

        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);

        System.out.println(json);

        MyClass readValue = mapper.readValue(json, MyClass.class);
        //Check the enum value was correctly deserialized
        Assert.assertEquals(readValue.getContext().get("key2"), MyEnum.enumValue1);
    }

}

オブジェクトは次のようなものにシリアル化されます。

[ "so_27871226.MyClass", {
  "val1" : "foo",
  "val2" : "var",
  "context" : [ "Java.util.HashMap", {
    "key3" : 3.0,
    "key2" : [ "so_27871226.Main$MyEnum", "enumValue1" ],
    "key1" : "stringValue1"
  } ]
} ]

そして、正しく逆シリアル化され、アサーションはパスします。

これを行う方法は他にもあります。詳細については、 https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization をご覧ください。

私はそれが役立つことを願っています。

20
fonkap