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.