简体   繁体   中英

[solved]How to use Jackson ObjectMapper to deserialize json list with type metadata?

jackson version: 2.11.1

I'm trying to serialize a List into json string and then deserialize it:

public class JacksonTypeTest {

    public static abstract class Pet {
        final String name;

        protected Pet(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    public static class Dog extends Pet {

        public Dog(@JsonProperty("name") String name) {
            super(name);
        }
    }

    public static class Cat extends Pet {

        public Cat(@JsonProperty("name") String name) {
            super(name);
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        List<Pet> pets = new ArrayList<>();
        pets.add(new Dog("Dog1"));
        pets.add(new Cat("Cat1"));

        ObjectMapper objectMapper = new ObjectMapper();

        // [the problem is below, see my answer]
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator());

        String json = objectMapper.writeValueAsString(pets);
        System.out.println(json);
        // output: [["com.hyd.jacksontest.JacksonTypeTest$Dog",{"name":"Dog1"}],["com.hyd.jacksontest.JacksonTypeTest$Cat",{"name":"Cat1"}]]

        ObjectReader listReader = objectMapper.readerForListOf(Pet.class);
        List<Pet> petList = listReader.readValue(json);
        // exception: Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException:
        // Unexpected token (START_ARRAY), expected VALUE_STRING:
        // need JSON String that contains type id (for subtype of java.util.List)

        System.out.println(petList);
    }
}

It fails at line List<Pet> petList = listReader.readValue(json);with message:

Unexpected token (START_ARRAY), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.util.List)

Output:

[["com.hyd.jacksontest.JacksonTypeTest$Dog",{"name":"Dog1"}],["com.hyd.jacksontest.JacksonTypeTest$Cat",{"name":"Cat1"}]]
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_ARRAY), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.util.List)
 at [Source: (String)"[["com.hyd.jacksontest.JacksonTypeTest$Dog",{"name":"Dog1"}],["com.hyd.jacksontest.JacksonTypeTest$Cat",{"name":"Cat1"}]]"; line: 1, column: 2]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1650)
    at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1400)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:155)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:96)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromArray(AsArrayTypeDeserializer.java:53)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserializeWithType(CollectionDeserializer.java:319)
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
    at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2057)
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1496)
    at com.hyd.jacksontest.JacksonTypeTest.main(JacksonTypeTest.java:52)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)

How to make the deserialization work?

Have you checked what is being deserialized by debugging. Is the json value same as your object?

Alternative code:

Create a wrapper object class which contains a list of Pet.class.

If you don't want to create a wrapper class, then you need to deserialize the json string to a List of type String and loop through the list, deserializing the strings to Pet class.

    List <String> petJsons = new ArrayList <> ();
    petJsons = mapper.readValue(json, List.class);

    for (Iterator <String> iterator = petJsons.iterator(); iterator.hasNext();) {
        String petJson = (String) iterator.next();
        pets.add(objectMapper.objectMapper.readValue(petJson, Pet.class));
    }

OK finally I figured it out.

The problem is, there is no type metadata for the root object .

To fix this, I need to change the code:

objectMapper.activateDefaultTyping(
    objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL
);

After adding a second parameter, the generated JSON string looks like this:

["java.util.ArrayList",[["com.hyd.jacksontest.JacksonTypeTest$Dog",{"name":"Dog1"}],["com.hyd.jacksontest.JacksonTypeTest$Cat",{"name":"Cat1"}]]]

This can be deserialized successfullly. The code now works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM