Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
moodysalem opened this issue Sep 27, 2016 · 2 comments
Milestone

Comments

@moodysalem
Copy link

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;
    }
}
@cowtowncoder
Copy link
Member

Thank you for reporting this, and providing full reproduction.

@cowtowncoder cowtowncoder changed the title JsonIdentityInfo: @id has to be the first key in deserialization when deserializing with @JsonCreator @JsonIdentityInfo: id has to be the first key in deserialization when deserializing with @JsonCreator Oct 5, 2016
cowtowncoder added a commit that referenced this issue Nov 29, 2016
@cowtowncoder
Copy link
Member

Interesting. Moving id as the second of 3 values passes ok, so it's only as last, or (more likely?) if it comes after all other properties are found, that this becomes problematic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants