Skip to content

@JsonIdentityInfo: id has to be the first key in deserialization when deserializing with @JsonCreator #1388

@moodysalem

Description

@moodysalem

This seems similar but different to issue #687. I'm seeing it in Jackson 2.8.3

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public static class NamedThing {
    private final UUID id;
    private final String name;

    @JsonCreator
    public NamedThing(@JsonProperty("id") UUID id, @JsonProperty("name") String name) {
        this.id = id;
        this.name = name;
    }

    public UUID getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        NamedThing that = (NamedThing) o;
        return Objects.equals(getId(), that.getId()) &&
            Objects.equals(getName(), that.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getName());
    }
}

@Test
public void testDeserializationFinalClassJSOG() throws IOException {
    final UUID id = UUID.fromString("a59aa02c-fe3c-43f8-9b5a-5fe01878a818");

    final NamedThing thing = new NamedThing(id, "Hello");

    final String jsog = om.writeValueAsString(Arrays.asList(thing, thing, thing));
    {
        final List<NamedThing> list = om.readValue(jsog, new TypeReference<List<NamedThing>>() {
        });
        assert list.stream().allMatch(thing::equals);
    }

    // this is the jsog representation of the list of 3 of the same item
    assertTrue(jsog.equals("[{\"@id\":1,\"id\":\"a59aa02c-fe3c-43f8-9b5a-5fe01878a818\",\"name\":\"Hello\"},1,1]"));

    // now move it around it have forward references
    final String forwardReferences = "[1,1,{\"@id\":1,\"id\":\"a59aa02c-fe3c-43f8-9b5a-5fe01878a818\",\"name\":\"Hello\"}]";
    // this works
    {
        final List<NamedThing> forward = om.readValue(forwardReferences, new TypeReference<List<NamedThing>>() {
        });
        assert forward.stream().allMatch(thing::equals);
    }

    // now move the @id to be not the first key in the object
    final String notFirstKey = "[1,1,{\"id\":\"a59aa02c-fe3c-43f8-9b5a-5fe01878a818\",\"name\":\"Hello\",\"@id\":1}]";
    // this fails
    {
        final List<NamedThing> forward = om.readValue(notFirstKey, new TypeReference<List<NamedThing>>() {
        });
        assert forward.stream().allMatch(thing::equals);
    }
}

Here is the exception

testDeserializationFinalClassJSOG(com.fastmodelsports.wtng.model.SerializationTests)  Time elapsed: 0.116 sec  <<< FAILURE!
com.fasterxml.jackson.databind.JsonMappingException: No Object Id found for an instance of com.fastmodelsports.wtng.model.SerializationTests$NamedThing, to assign to property '@id'
 at [Source: [1,1,{"id":"a59aa02c-fe3c-43f8-9b5a-5fe01878a818","name":"Hello","@id":1}]; line: 1, column: 66] (through reference chain: java.util.ArrayList[0])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:261)
    at com.fasterxml.jackson.databind.DeserializationContext.reportUnresolvedObjectId(DeserializationContext.java:1259)
    at com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer.handleIdValue(PropertyValueBuffer.java:242)
    at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:140)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:401)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1196)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:314)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1167)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:146)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:277)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:249)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2852)
    at com.fastmodelsports.wtng.model.SerializationTests.testDeserializationFinalClassJSOG(SerializationTests.java:239)

When I change the model to this, the same test code succeeds

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public static class NamedThing {
    private UUID id;
    private String name;

    public NamedThing() {
    }

    public NamedThing(UUID id, String name) {
        this.id = id;
        this.name = name;
    }

    public UUID getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        NamedThing that = (NamedThing) o;
        return Objects.equals(getId(), that.getId()) &&
            Objects.equals(getName(), that.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getName());
    }

    public void setId(UUID id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions