Jackson JSON List with Object Type

前端 未结 3 796
广开言路
广开言路 2020-12-31 09:53

I have to serialize JSON from a list of Objects. The resulting JSON has to look like this:

{
    \"status\": \"success\",
    \"models\": [
        {
                


        
相关标签:
3条回答
  • 2020-12-31 10:21

    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.

    0 讨论(0)
  • 2020-12-31 10:29

    Another approach is using StdConverter class. Here is a working (abbreviated) example:

    // MyParentObject.java
    import 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.java
    
    import 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.java
    
    import 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.

    0 讨论(0)
  • 2020-12-31 10:45

    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.

    0 讨论(0)
提交回复
热议问题