question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Deserialize Json Array that contin 2 objects with the same ID - JsonMappingException: Already had POJO for id

See original GitHub issue

How can I deserialize an Array with two objects that contains the same SubObject with the same Id?

For example this is the JSON of the array

[
    {
        "@id": 98,
        "age": 29,
        "name": "mkyong",
        "relatedPackage": {"@id":99, "receivedOn":1374012807237 }
    },
    {
        "@id": 101,
        "age": 25,
        "name": "luis",
        "relatedPackage": {"@id":99, "receivedOn":1374012807237 }
    }
]

As you can see both objects has the same related package. I want that the deserializer only parse the first relatedPackage and use it for the secon relatedPackage.

My Pojos are the next:

User Class:

package com.mkyong.core;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="@id")
public class User {

    public int age;
    public String name;
    public RelatedPackage relatedPackage;

    //getter and setter methods

    @Override
    public String toString() {
        return "User [age=" + age + ", name=" + name + ", " +
                "messages=" + relatedPackage + "]";
    }
}

RelatedPackage Class:

package com.mkyong.core;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class,property="@id")
public class RelatedPackage {
    public long receivedOn;

    public String toString() {
        return "{ receivedOn: " + receivedOn + " }";
    }
}

And finaly the main Object wich realize the deserializtion:

package com.mkyong.core;

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;


public class JacksonExample {
    public static void main(String[] args) {

    ObjectMapper mapper = new ObjectMapper();

    try {

        // read from file, convert it to user class
        File file = new File("/home/lvargas/user.json");
        User[] userList = mapper.readValue(file, User[].class);

        // display to console
        System.out.println(userList);

    } catch (JsonGenerationException e) {

        e.printStackTrace();

    } catch (JsonMappingException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

  }

}

When both relatedPackage has the same Id it sends me the next error message

com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.lang.Integer) [99] (through reference chain: com.mkyong.core.User["relatedPackage"]->com.mkyong.core.RelatedPackage["@id"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:197)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1332)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:252)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:115)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:449)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:107)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:250)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:115)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:151)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:18)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1988)
    at com.mkyong.core.JacksonExample.main(JacksonExample.java:20)
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.Integer) [99]
    at com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.bindItem(ReadableObjectId.java:27)
    at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:91)
    at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:80)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:250)
    ... 10 more

I understand that it is trying to create a second object with the same objectId.

How can I solve that problem?

Issue Analytics

  • State:closed
  • Created 10 years ago
  • Comments:35 (14 by maintainers)

github_iconTop GitHub Comments

21reactions
maludwigcommented, Nov 12, 2018

I’m not sure if this remains an open issue or not, but I figured it might help to have a minimal project with an obvious use-case. So I’ve cut down a project I’m working on here to be as small as I can make it.

I want to make a library API. Where users can add Books, which have a ManyToMany relationship with Authors. A book may have multiple Authors, and an Author may write multiple books, so if we serialize a book to JSON and include all of the information about all of its Authors, and then we serialize all of those, and then they all have information about their Books, so then we serialize all those…

An Author is very simple, the name of the author is the ID. That’s it. A Book is an ID, and a title. They have a ManyToMany relationship between them.

So we could POST a book like this, which would make a book, and map it to an author, which we may already have in the database, or which we might be making from her scratch:

POST /book
{
    "title": "Harry Potter and the Philosopher's Stone",
    "authors": [
        {
            "name": "JK Rowling"
        }
    ]
}

Works great! But then we would need to make one HTTP request per book, which could incur a performance constraint. So we want to have another endpoint for adding lots of them:

POST /books
[
    {
        "title": "Harry Potter and the Philosopher's Stone",
        "authors": [
            {
                "name": "JK Rowling"
            }
        ]
    },
    {
        "title": "Harry Potter and the Chamber of Secrets",
        "authors": [
            {
                "name": "JK Rowling"
            }
        ]
    }
]

Unfortunately this will be a problem, because Jackson will deserialize the two “JK Rowling” separately, and bomb out, and even though they are the exact same object, Jackson doesn’t understand how to map them together. My solution was to have the two objects simply resolve to be the same thing. The exact same object reference.

In the Entity:

    @JsonIdentityInfo(
            generator=ObjectIdGenerators.PropertyGenerator.class, 
            property="name", 
            scope = Author.class, 
            resolver = DedupingObjectIdResolver.class
    )

The ObjectIdResolver class:

package hello;

import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey;
import com.fasterxml.jackson.annotation.ObjectIdResolver;
import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;

import java.util.HashMap;

public class DedupingObjectIdResolver extends SimpleObjectIdResolver {
    @Override
    public void bindItem(IdKey id, Object ob) {
        if (_items == null) {
            _items = new HashMap<>();
        }
        _items.put(id, ob);
    }

    @Override
    public ObjectIdResolver newForDeserialization(Object context) {
        return new DedupingObjectIdResolver();
    }
}

I’ll upload the final project code in a sec here.

15reactions
cowtowncodercommented, Jul 17, 2013

You can send your QUESTIONS to mailings lists. Issue trackers are for reporting problems and requesting new features.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Deserialize Json Array that contin 2 objects with the same ID
I want that the deserializer only parse the first relatedPackage and use it for the secon relatedPackage. My Pojos are the next: User...
Read more >
Unable to deserialize Json that contain 2 objects with the ...
I have used jackson JsonIdentityInfo to handle the recursive object reference in spring mvc. I came across one issue i.e., Unable to deserialize...
Read more >
DeserializationFeature (jackson-databind 2.11.0 API)
Enumeration that defines simple on/off features that affect the way Java objects are deserialized from JSON. Note that features can be set both...
Read more >
All You Need To Know About JSON Parsing With Jackson
It allows us to do conversion between POJOs and JSON documents using property accessors or using annotations. It offers two types of binding:....
Read more >
Line-delimited JSON with Jackson - cowtowncoder - Medium
JSON specification defines “JSON Values” as just “[JSON] Objects and Arrays” (*), and most JSON usage examples focus on that: for example, how...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found