问题
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?
回答1:
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:
- How do I parametrize response parsing in Java?
- Deserializing or serializing any type of object using Jackson ObjectMapper and handling exceptions
来源:https://stackoverflow.com/questions/57975814/refactor-method-to-use-generic-for-deserialization