Force a property to serialize into identity in Jackson

336 views Asked by At

I have a class with

(disclaimer, I am writing in Kotlin, but I translate it into Java so more people can read it)

@JsonIdentityInfo(
    ObjectIdGenerators.PropertyGenerator.class,
    "id"
)
public class Foo implements Serializable {
    public int id;
    public List<Foo> fooList;
}

I want to serialize another object which contains a list of Foos, but I want all entries in the serialized fooList to be IDs, not objects. One solution that came to mind was to sort them topologically in reverse order before serialization, as they form a directed acyclic graph. But that's obviously not a perfect solution, so I'm wondering if there's an annotation or an otherwise clean Jackson way to do something like that.

EDIT:
The class that contains a list of Foo looks like this:

public class Bar implements Serializable {
    public List<Foo> fooList;
}

And when I deserialize a Bar instance from this JSON:

{
  "fooList": [
    {
      "id": 0,
      "fooList": [1]
    }, 
    {
      "id": 1,
      "fooList": []
    }
  ]
}

And then I serialize it back, I want the output to be the same as the input, but instead it's this:

{
  "fooList": [
    {
      "id": 0,
      "fooList": [
        {
          "id": 1,
          "fooList": []
        }
      ]
    },
    1
  ]
}
1

There are 1 answers

0
f-CJ On BEST ANSWER

You could create a class that extends from Foo class to offer an alternative serialization for fooList using the annotation @JsonGetter, like a wrapper.

Foo class:

public class Foo implements Serializable {
    private int id;
    private List<Foo> fooList;

    public int getId() {
        return id;
    }

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

    public List<Foo> getFooList() {
        return fooList;
    }

    public void setFooList(List<Foo> fooList) {
        this.fooList = fooList;
    }
}

Bar class:

public class Bar implements Serializable {
    public List<FooJsonSimplifiedSerializationWrapper> fooList;

    public List<FooJsonSimplifiedSerializationWrapper> getFooList() {
        return fooList;
    }

    public void setFooList(List<FooJsonSimplifiedSerializationWrapper> fooList) {
        this.fooList = fooList;
    }
}

FooFooJsonSimplifiedSerializationWrapper is the Foo wrapper for serialization and it has a method to convert from Lst<Foo> to List<FooFooJsonSimplifiedSerializationWrapper> that you will have to call at some point before serializing:

public class FooJsonSimplifiedSerializationWrapper extends Foo {
    @JsonGetter("fooList")
    public List<Integer> serializeFooList() {
        return this.getFooList().stream().map(f -> f.getId()).collect(Collectors.toList());
    }

    public static List<FooJsonSimplifiedSerializationWrapper> convertFromFoo(List<Foo> fooList) {
        return fooList.stream().map(f -> {
            FooJsonSimplifiedSerializationWrapper fooSimplified = new FooJsonSimplifiedSerializationWrapper();
            BeanUtils.copyProperties(f, fooSimplified);

            return fooSimplified;

        }).collect(Collectors.toList());
    }
}

Main with some tests:

public static void main(String[] args) throws IOException {
    Foo foo = new Foo();
    foo.setId(1);

    Foo fooChild = new Foo();
    fooChild.setId(2);
    fooChild.setFooList(new ArrayList<>());

    Foo fooChild2 = new Foo();
    fooChild2.setId(3);
    fooChild2.setFooList(new ArrayList<>());

    foo.setFooList(Arrays.asList(fooChild, fooChild2));

    Bar bar = new Bar();
    bar.setFooList(FooJsonSimplifiedSerializationWrapper.convertFromFoo(Arrays.asList(foo)));

    System.out.println(new ObjectMapper().writeValueAsString(foo));
    System.out.println(new ObjectMapper().writeValueAsString(bar));
}

This code will print:

Foo serialization: {"id":1,"fooList":[{"id":2,"fooList":[]},{"id":3,"fooList":[]}]}

Bar serialization: {"fooList":[{"id":1,"fooList":[2,3]}]}

Another solution could involve using Views with @JsonView annotation and customizing the views to adapt the serialization to your needs but in my opinion is a more cumbersome solution.