Как мне использовать Jackson для преобразования объекта в карту с использованием ввода по умолчанию?

#java #json #jackson

#java #json #jackson

Вопрос:

Я использую Jackson для сериализации аннотированного объекта JAXB в объект карты. Вот некоторый код, иллюстрирующий мою проблему:

     public class Test {

    @XmlAccessorType(XmlAccessType.NONE)
    public static class Inner {
        @XmlAttribute
        public int foo;
    }

    @XmlAccessorType(XmlAccessType.NONE)
    public static class Outer {
        @XmlAttribute
        public String bar;

        @XmlElement
        public Inner in;
    }

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

        mapper.enableDefaultTyping();

        Inner in = new Inner();
        in.foo = 42;
        Outer out = new Outer();
        out.in = in;
        out.bar = "thecakeisalie";

        Object o = mapper.convertValue(out, TreeMap.class);
        System.out.println(o);
    }

    public static ObjectMapper getMapper() {
        ObjectMapper mapper = new ObjectMapper();
        AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
        mapper.setAnnotationIntrospector(introspector);
        return mapper;
    }
}
 

Это приводит к следующей ошибке:

 Exception in thread "main" java.lang.IllegalArgumentException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object
 at [Source: N/A; line: -1, column: -1]
    at org.codehaus.jackson.map.ObjectMapper._convert(ObjectMapper.java:2493)
    at org.codehaus.jackson.map.ObjectMapper.convertValue(ObjectMapper.java:2459)
    at com.example.test.Test.main(Test.java:49)
Caused by: org.codehaus.jackson.map.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object
 at [Source: N/A; line: -1, column: -1]
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.wrongTokenException(StdDeserializationContext.java:261)
    at org.codehaus.jackson.map.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:100)
    at org.codehaus.jackson.map.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:86)
    at org.codehaus.jackson.map.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:69)
    at org.codehaus.jackson.map.deser.std.UntypedObjectDeserializer.deserializeWithType(UntypedObjectDeserializer.java:106)
    at org.codehaus.jackson.map.deser.std.MapDeserializer._readAndBind(MapDeserializer.java:321)
    at org.codehaus.jackson.map.deser.std.MapDeserializer.deserialize(MapDeserializer.java:249)
    at org.codehaus.jackson.map.deser.std.MapDeserializer.deserialize(MapDeserializer.java:33)
    at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2695)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1308)
    at org.codehaus.jackson.map.ObjectMapper._convert(ObjectMapper.java:2489)
    ... 2 more
 

Если строка mapper.enableDefaultTyping(); опущена, код работает и выводит следующее:

{bar=thecakeisalie, in={foo=42}}

Если я использую mapper для сериализации в json, он будет работать с вводом по умолчанию.

Ввод по умолчанию несовместим с преобразованиями объектов или я использую его неправильно?

Ответ №1:

ОК. Хм. Проблема связана с несовместимым использованием: происходит то, что при сериализации ввод по умолчанию добавляет только информацию о типе в соответствии с его конфигурацией, и в этом случае информация о типе не включается (типы не являются абстрактными и не объявляются как java.lang.Объект). Но при десериализации (как части преобразования) тип результата ожидает информацию о типе, поскольку номинальный тип равен TreeMap<Object,Object> ; и вот откуда возникает исключение.

Но поскольку вы конвертируете в карту, которая по сути является «нетипизированным» типом, вам лучше просто не включать ввод по умолчанию. Эта информация о типе будет отброшена в любом случае.

Или, если вы действительно хотите увидеть эту включенную информацию о типе, вам нужно выполнить двухфазную обработку: сериализовать с включенным вводом по умолчанию (с помощью mapper, у которого он включен) и десериализовать как TreeMap с ObjectMapper, у которого отключен ввод по умолчанию.