Jackson JSON List with Object Type
There's no built-in way to do this. You'll have to write your own JsonSerializer
. Something like
class ModelSerializer extends JsonSerializer<List<Model>> { @Override public void serialize(List<Model> value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeStartArray(); for (Model model : value) { jgen.writeStartObject(); jgen.writeObjectField("model", model); jgen.writeEndObject(); } jgen.writeEndArray(); }}
and then annotate the models
field so that it uses it
@JsonSerialize(using = ModelSerializer.class)private List<Model> models;
This would serialize as
{ "status": "success", "models": [ { "model": { "id": 1, "color": "red" } }, { "model": { "id": 2, "color": "green" } } ]}
If you're both serializing and deserializing this, you'll need a custom deserializer as well.
This is an oldish question, But there is an arguably more idiomatic way of implementing this (I'm using jackson-databind:2.8.8
):
Define a ModelSerializer
(That extends StdSerializer
as recommended by Jackson) that prints your model how you like and use the @JsonSerialize(contentUsing = ...)
over your collection type:
class ModelSerializer extends StdSerializer<Model> { public ModelSerializer(){this(null);} public ModelSerializer(Class<Model> t){super(t);} // sets `handledType` to the provided class @Override public void serialize(List<Model> value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartObject(); jgen.writeObjectField("model", value); jgen.writeEndObject(); }}
Meanwhile, in another file:
class SomethingWithModels { // ... @JsonSerialize(contentUsing = ModelSerializer.class) private Collection<Model> models; // ...}
Now you aren't bound to just List
s of models but may apply this to Collection
s, Set
s, Native []
s and even the values of Map
s.
Another approach is using StdConverter
class. Here is a working (abbreviated) example:
// MyParentObject.javaimport com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.annotation.JsonDeserialize;import com.fasterxml.jackson.databind.annotation.JsonSerialize;public class MyParentObject { @JsonSerialize(converter = ChildListToString.class) @JsonDeserialize(converter = StringToChildList.class) @JsonProperty public List<AChildObject> myChildren;}
// ChildListToString.javaimport com.fasterxml.jackson.databind.util.StdConverter;import java.util.List;import java.util.stream.Collectors;public class ChildListToString extends StdConverter<List<AChildObject>, String> { @Override public String convert(List<IntakeModuleUrn> value) { // this is just as effective as using Jackson "write array" // Try-Catch omitted for brevity StringBuilder builder = new StringBuilder("["); value.stream().map(value -> new ObjectMapper().writeValue(value)).forEach(urnStr -> { if(builder.length() > 1) { builder.append(", "); } builder.append("\"").append(urnStr).append("\""); }); builder.append("]"); return builder.toString(); }}
// StringToChildList.javaimport com.fasterxml.jackson.core.JsonParseException;import com.fasterxml.jackson.core.type.TypeReference;import com.fasterxml.jackson.databind.JsonMappingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.util.StdConverter;import java.io.IOException;import java.net.URISyntaxException;import java.util.Collections;import java.util.List;import java.util.stream.Collectors;public class StringToChildList extends StdConverter<String, List<AChildObject>> { @Override public List<AChildObject> convert(String value) { // try - catch omitted here for brevity List<String> strings = new ObjectMapper().readValue(value, new TypeReference<List<String>>() {}); return strings.stream() .map(string -> { return new ObjectMapper().readValue(string, AChildObject.class) }).collect(Collectors.toList()); }}
I like this because it gives you control of serialization and deserialization separately.