Jackson Tips: custom List serialization

(how to serialize contents of List<String> in alphabetic order, using “helper” List subtype, custom JsonSerializer)

On Customizing Jackson value Serialization

Jackson-databind allows a wide range of customization options for formatting output of many types, both using configuration settings and annotations. But there are sometimes cases where more customization is necessary.

ObjectMapper mapper = JsonMapper.builder()
.addModule(new SimpleModule()
.addSerializer(new MyValueSerializer()
).build();

Use Case: Alphabetically ordering List<String> on serialization

So, let’s consider a case that I know has been requested as a feature (but not yet implemented as such): ability to make contents of a List to be serialized in their natural (for Strings, lexicographic (~= alphabetic)) order:

public class Values {
// Ids should be sorted alphabetically when serialized:
public List<String> ids;
public Values(String... ids) {
this.ids = Arrays.asList(ids);
}
}
ObjectMapper mapper = new JsonMapper();
assertEquals("{\"ids\":[\"a\",\"a",\"b\",\"c\"]}",
mapper.writeValueAsString(new Values("a", "c", "a", "b"))
);
  1. We do not need any general Jackson configuration to be applied
  2. No polymorphism (String is not polymorphic and List typically neither, specifically not here)
static class SortedListSerializer
extends JsonSerializer<List<String>>
{
public SortedListSerializer() { super(List.class, false); }
@Override
public void serialize(List<String> value, JsonGenerator g, SerializerProvider provider)
throws IOException
{
List<String> sorted = ...; // sort the entries into new list
g.writeStartArray();
for (String value : sorted) {
g.writeString(value);
}
g.writeEndArray();
}
}

Registering Custom List<String> serializer

How to register our custom serializer depends a little bit on exactly how we want it to apply. A simple mechanism if this is needed only in one or at most a few places; just attach it to property like so:

public class Values {
// note: for Lists, "using" serializes "List" itself,
// "contentUsing" would be used for value elements
@JsonSerialize(using = SortedListSerializer.class)
public List<String> ids;
}

Using @JsonSerialize as Class annotation for Fun & Profit

So, yes: @JsonSerialize is applicable to Classes. But wait a minute — we are talking about List type, which is part of JDK!
How could we annotate that class?

@JsonSerialize(using = SortedListSerializer.class)
public class SortedStringList extends ArrayList<String> {
public SortedStringList(String... ids) {
super(Arrays.asList(ids));
}
}
String jsonForRoot = mapper.writeValueAsString(
new SortedStringList("foo", "bar"));
// -> ["bar","foo"]
// Note: SortedValues uses type "SortedStringList" for "values":
String jsonForValues = mapper.writeValueAsString(
new SortedValues("b", "d", "a"));
// -> {"value":["a","b","d"]}
  • We may want to remove duplicates
  • We may want to produce a JSON String as concatenation of a List (serializers are not limited to “natural” mapping)
  • Perhaps we just wanted to write out number of Items in List as a count (size)

Helper Types For the Win!

Bit more generally, this technique of using “helper” (or “throw-away” or “one-off”) types is applicable more generally. It is especially powerful in avoiding dreaded “Generic Types as Root Values” problem where, for example:

@JsonTypeInfo(...) // we have a polymorphic type!
abstract class Animal {
}
public class Dog extends Animal { ... }List<Animal> animals = ...
String json = mapper.writeValueAsString(animals);
// PROBLEM! Why did my Animal values NOT get serialized as polymorphic?!?!// Solution ->public class AnimalList extends ArrayList<Animal> { }
  1. Construct a TypeReference (or JavaType), construct an ObjectWriter with it (ObjectMapper.writerFor(new TypeReference<List<Animal>>() { }), use that
  1. Add/override (Jackson) annotations
  2. Be configured separately from base types for settings not controlled by annotations

Open Source developer, most known for Jackson data processor (nee “JSON library”), author of many, many other OSS libraries for Java, from ClassMate to Woodstox