Jackson JSON List with Object Type Jackson JSON List with Object Type json json

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 Lists of models but may apply this to Collections, Sets, Native []s and even the values of Maps.


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.