복붙노트

[SPRING] Jackson : 각 값에 대해 올바른 유형의 Map <String, Object>에 비순환

SPRING

Jackson : 각 값에 대해 올바른 유형의 Map 에 비순환

다음과 같은 클래스가 있습니다.

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

JSON에서 뒤로 JSON으로 Jackson과 왕복 여행을 할 수 있기를 기대하고 있습니다. 위의 객체를 직렬화하여 다음 출력을받을 수 있습니다.

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

내가 겪고있는 문제는 직렬화 된 맵의 값에 유형 정보가 없으므로 올바르게 직렬화되지 않는다는 것입니다. 예를 들어, 위의 예제에서 enumValue1은 열거 형 값으로 deserialize되어야하지만 String 대신 deserialize됩니다. 다양한 유형에 어떤 유형을 사용하는지에 대한 예제를 보았습니다. 그러나 시나리오에서는 유형이 무엇인지 (내가 미리 알지 못하는 사용자 생성 객체가 될 것임) 알 수 없으므로 필요할 수 있습니다. 키 값 쌍으로 유형 정보를 직렬화 할 수 있습니다. 잭슨과 어떻게이 일을 성취 할 수 있습니까?

기록을 위해 Jackson 버전 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);
}

이것은 스프링 기반 프로젝트이기 때문에 매퍼는 다음과 같이 생성됩니다.

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

해결법

  1. ==============================

    1.나는 당신이 원하는 것을 성취하는 가장 간단한 방법이 다음을 사용한다고 생각합니다 :

    나는 당신이 원하는 것을 성취하는 가장 간단한 방법이 다음을 사용한다고 생각합니다 :

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

    그리고 올바르게 다시 deserialize 될 것이며, 주장이 통과 할 것입니다.

    여기에 더 많은 방법이 있습니다. 자세한 내용은 https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization을 참조하십시오.

    나는 그것이 도움이되기를 바랍니다.

  2. from https://stackoverflow.com/questions/27871226/jackson-deserialize-to-a-mapstring-object-with-correct-type-for-each-value by cc-by-sa and MIT license