-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Ability to sort Sets before serialization (add SerializationFeature.ORDER_SET_ELEMENTS) #3166
Description
I have a POJO with an array of Comparable types (DayOfWeek in my case). This is logically a set, an it is therefore represented as such in the object. When serialized using an unordered set (such as a HashSet), the elements are written to JSON in the set's arbitrary iteration order, and not in a logical sorted order. This makes the data more annoying to work with: it's harder to see what elements are present at a visual glance, and any tests on the JSON structure itself need to make sure they're ignoring the ordering of the resulting array.
Set<DayOfWeek> unorderedSet = new HashSet<>(EnumSet.allOf(DayOfWeek.class));
JsonMapper jsonMapper = JsonMapper.builder().build();
System.out.println(jsonMapper.writeValueAsString(unorderedSet));
// ["WEDNESDAY","MONDAY","THURSDAY","SUNDAY","FRIDAY","TUESDAY","SATURDAY"]
Set<DayOfWeek> orderedSet = EnumSet.allOf(DayOfWeek.class);
System.out.println(jsonMapper.writeValueAsString(orderedSet));
// ["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]What I would like is the ability to use the natural order of the Set elements when outputting the array to JSON, i.e. something equivalent to ORDER_MAP_ENTRIES_BY_KEYS, but for collection entries, not map entries. In a perfect world, I guess it would be nice to be able to specify it globally for the ObjectMapper, as well as locally per property, but either would work. null values in the set should ideally be supported as well, even though they're not comparable, though this isn't a showstopper for my use case.
This might be a separate issue, but I'd like to be able to the inverse as well: when deserializing from a JSON array, produce a set with the desired order (e.g. with a LinkedHashSet).
Workaround
A simple custom Converter can be created to sort the elements on (de)serialization:
private static class MyPojo {
// Constructors, getters, setters
@JsonSerialize(converter = OrderedSetConverter.class)
@JsonDeserialize(converter = OrderedSetConverter.class)
private Set<DayOfWeek> daysOfWeek;
}
private static class OrderedSetConverter extends StdConverter<Set<DayOfWeek>, Set<DayOfWeek>> {
@Override
public Set<DayOfWeek> convert(Set<DayOfWeek> value) {
return value == null ? null : value.stream()
.sorted(Comparator.nullsLast(Comparator.naturalOrder()))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
}There's probably a way to make it generic on <E extends Comparable<? super E>>, but the naive approach ran into some Jackson type issues, and I haven't yet taken the time to dig deeper to figure out the proper Jackson way to handle it.