I have a Java object containing an unsorted set of comparable objects. Is there any way to make Jackson sort this set before serializing it into a JSON list?
I want this behavior so that objects that are equal when represented in Java deterministically produce the same JSON.
Is there some setting in Jackson that can do this? Or is my best bet to write a custom serializer?
Can a single custom serializer work for all Set<T>
where T implements Comparable
?
This question is not a duplicate of jackson to sort the response by using the field name alone, which asks how to sort keys in a Map. This question is about sorting elements in a Set.
Note that Jackson does not use java. io. Serializable for anything: there is no real value for adding that. It gets ignored.
Jackson by default uses the getters for serializing and setters for deserializing.
Jackson is a solid and mature JSON serialization/deserialization library for Java. The ObjectMapper API provides a straightforward way to parse and generate JSON response objects with a lot of flexibility. This article discussed the main features that make the library so popular.
Serialize Null Fields Fields/PropertiesWith its default settings, Jackson serializes null-valued public fields. In other words, resulting JSON will include null fields. Here, the name field which is null is in the resulting JSON string.
Even if you use HashSet
implementation of Set
interface it produces the same result when two sets contain the same objects. So, you do not need to sort set to produce the same JSON for two different but equal (contain same objects) sets.
But if you want to force Jackson to sort collection before serialisation you need to implement custom serialiser.
We can only register serialiser for Set
and it will be used for sets which contain Comparable
objects and for sets which do not so it must be checked in custom implementation.
Example implementation could look like below:
class SortedSetJsonSerializer extends JsonSerializer<Set> {
@Override
public void serialize(Set set, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (set == null) {
gen.writeNull();
return;
}
gen.writeStartArray();
if (!set.isEmpty()) {
// create sorted set only if it itself is not already SortedSet
if (!SortedSet.class.isAssignableFrom(set.getClass())) {
Object item = set.iterator().next();
if (Comparable.class.isAssignableFrom(item.getClass())) {
// and only if items are Comparable
set = new TreeSet(set);
}
}
for (Object item : set) {
gen.writeObject(item);
}
}
gen.writeEndArray();
}
}
And example usage:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.IntStream;
public class JsonApp {
public static void main(String[] args) throws Exception {
Set<Integer> ints = newSet();
SimpleModule module = new SimpleModule();
module.addSerializer(Set.class, new SortedSetJsonSerializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(new MyModel(ints)));
}
private static Set<Integer> newSet() {
Set<Integer> ints = new HashSet<>();
IntStream.range(10, 20).forEach(ints::add);
return ints;
}
}
class MyModel {
private Set<Integer> integers;
public MyModel(Set<Integer> integers) {
this.integers = integers;
}
public Set<Integer> getIntegers() {
return integers;
}
public void setIntegers(Set<Integer> integers) {
this.integers = integers;
}
}
prints:
{"integers":[10,11,12,13,14,15,16,17,18,19]}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With