1

Here is the method that I want to refactor:

public static List<ComponentPOCO> parseJsonComponentFromString(String fileContents){

        try {
            ObjectMapper mapper = new ObjectMapper()
                    .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            List<ComponentPOCO> component = mapper.readValue(fileContents, new TypeReference<List<ComponentPOCO>>() {});
            return component;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

I'm attempting to refactor my deserialization method to use generics to enable me to deserialize any type. I can do this just fine for objects that are not in a collection, like this:

public static <T> T parseProductData(String jsonData, Class<T> typeClass) throws IOException, IllegalAccessException {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    T inputMessage = objectMapper.readValue(jsonData, typeClass);
    return inputMessage;
}

Here is an example of the data that I'm deserializing into the ComponentPOCO class:

[
      {   "artifactPathOrUrl": "http://www.java2s.com/Code/JarDownload/sample/sample.jar.zip",
        "namespace": "exampleNamespace1",
        "name": "exampleName1",
        "tenant": "exampleTenant1"
      },

      {   
        "artifactPathOrUrl": "http://www.java2s.com/Code/JarDownload/sample-calculator/sample-calculator-bundle-2.0.jar.zip",
        "namespace": "exampleNamespace1",
        "name": "exampleName2",
        "tenant": "exampleTenant1"
      },
      {   
        "artifactPathOrUrl": "http://www.java2s.com/Code/JarDownload/helloworld/helloworld.jar.zip",
        "namespace": "exampleNamespace1",
        "name": "exampleName3",
        "tenant": "exampleTenant1"
      },
      {   
        "artifactPathOrUrl": "http://www.java2s.com/Code/JarDownload/fabric-activemq/fabric-activemq-demo-7.0.2.fuse-097.jar.zip",
        "namespace": "exampleNamespace1",
        "name": "exampleName4",
        "tenant": "exampleTenant1"
      }
]

Here is the code of the ComponentPOCO type:

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import lombok.experimental.Accessors;
import org.apache.pulsar.common.io.SinkConfig;
import org.apache.pulsar.common.io.SourceConfig;

import java.util.List;
import java.util.Map;

@Setter
@Getter
@EqualsAndHashCode
@ToString
@Accessors(chain = true)
@Data
public class ComponentPOCO {
    @JsonProperty
    private String namespace;
    @JsonProperty
    private String tenant;
    @JsonProperty
    private String name;
    @JsonProperty
    private String type;
    @JsonProperty
    private String destinationTopicName;
    @JsonProperty
    private String artifactPathOrUrl;
    @JsonProperty
    private String className;
    @JsonProperty
    private List<String> inputs;
    @JsonProperty
    private String output;
    @JsonProperty
    private Map<String, Object> userConfig;
    @JsonProperty
    private String logTopic;
    @JsonProperty
    private Map<String, Object> configs;
    @JsonProperty
    private Integer parallelism;
    @JsonProperty
    public String sinkType;
    @JsonProperty
    private String sourceType;
    @JsonProperty
    public String runtimeFlags;
}

Is there a way to enable me to deserialize an entire list using generics like this?

devinbost
  • 4,658
  • 2
  • 44
  • 57
  • 3
    already answered here: https://stackoverflow.com/questions/6349421/how-to-use-jackson-to-deserialise-an-array-of-objects#comment7431246_6349421 –  Sep 17 '19 at 14:16

1 Answers1

0

You can extract all kind of interaction with Jackson to extra class and hide all difficulties there. ObjectMapper can be created and configured once. For single object you can use readValue(String content, Class<T> valueType) method and for collection you need to build TypeReference and use readValue(String content, TypeReference valueTypeRef) method. Simple implementation could look like below:

class JsonMapper {

  private final ObjectMapper mapper = new ObjectMapper();

  public JsonMapper() {
    // configure mapper instance if required
    mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    // etc...
  }

  public String serialise(Object value) {
    try {
      return mapper.writeValueAsString(value);
    } catch (JsonProcessingException e) {
      throw new IllegalStateException("Could not generate JSON!", e);
    }
  }

  public <T> T deserialise(String payload, Class<T> expectedClass) {
    Objects.requireNonNull(payload);
    Objects.requireNonNull(expectedClass);

    try {
      return mapper.readValue(payload, expectedClass);
    } catch (IOException e) {
      throw new IllegalStateException("JSON is not valid!", e);
    }
  }

  public <T> List<T> deserialiseList(String payload, Class<T> expectedClass) {
    Objects.requireNonNull(payload);
    Objects.requireNonNull(expectedClass);

    try {
      return mapper.readValue(payload, constructListTypeOf(expectedClass));
    } catch (IOException e) {
      throw new IllegalStateException("JSON is not valid!", e);
    }
  }

  private <T> CollectionType constructListTypeOf(Class<T> expectedClass) {
    return mapper.getTypeFactory().constructCollectionType(List.class, expectedClass);
  }
}

And usage:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

public class JsonApp {

  public static void main(String[] args) {
    JsonMapper mapper = new JsonMapper();
    System.out.println(mapper.deserialise("{\"id\":1233}", A.class));
    System.out.println(mapper.deserialiseList("[{\"id\":4567}]", A.class));
  }
}

class A {
  private int id;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  @Override
  public String toString() {
    return "A{" + "id=" + id + '}';
  }
}

Above code prints:

A{id=1233}
[A{id=4567}]

See also:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146