I am using the Jackson
for the Deseilization of the JSON. The Deseilization
works perfectly for a JSON with CustomerDocument
. However, I have a new requirement in which I need to find whether provided JSON has CustomerDocument
or just Customer
.
I am able to develop the logic for both but the problem is that when I try to merge it won't work for CustomerDocument
. I am looking for a solution that would work for both. All I would like to do is build the logic to differentiate the incoming JSON based on customerDocument
and single Customer
.
Following is the CustomerDocument
JSON:
{
"isA": "CustomerDocument",
"customerList": [
{
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
]
}
Customer.class:
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer {
private String isA;
private String name;
private String age;
}
JacksonMain:
public class JacksonMain {
public static void main(String[] args) throws IOException {
final InputStream jsonStream = JacksonMain.class.getClassLoader().getResourceAsStream("Customer.json");
final JsonParser jsonParser = new JsonFactory().createParser(jsonStream);
final ObjectMapper objectMapper = new ObjectMapper();
jsonParser.setCodec(objectMapper);
//Goto the start of the document
jsonParser.nextToken();
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
jsonParser.nextToken();
}
jsonParser.nextToken();
//Loop through each object within the customerList and deserilize them
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
final JsonNode customerNode = jsonParser.readValueAsTree();
final String eventType = customerNode.get("isA").asText();
Object event = objectMapper.treeToValue(customerNode, Customer.class);
System.out.println(event.toString());
}
}
}
The above code works perfectly and produces the following result:
Customer(isA=Customer, name=Batman, age=2008)
Scenario-2
Now user can provide the direct customer
object without the customerDocument
. Something like this:
{
"isA": "Customer",
"name": "Superman",
"age": "2013"
}
'Customer.class' would remain the same and JacksonMain
would be modified to:
public class JacksonMain {
public static void main(String[] args) throws IOException {
final InputStream jsonStream = JacksonMain.class.getClassLoader().getResourceAsStream("Customer.json");
final JsonParser jsonParser = new JsonFactory().createParser(jsonStream);
final ObjectMapper objectMapper = new ObjectMapper();
jsonParser.setCodec(objectMapper);
//Goto the start of the document
jsonParser.nextToken();
final JsonNode jsonNode = jsonParser.readValueAsTree();
final String inputType = jsonNode.get("isA").asText();
if (inputType.equalsIgnoreCase("Customer")) {
Object singleCustomer = objectMapper.treeToValue(jsonNode, Customer.class);
System.out.println(singleCustomer.toString());
} else if (inputType.equalsIgnoreCase("CustomerDocument")) {
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
jsonParser.nextToken();
}
jsonParser.nextToken();
//Loop through each object within the customerList and deserilize them
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
final JsonNode customerNode = jsonParser.readValueAsTree();
final String eventType = customerNode.get("isA").asText();
Object event = objectMapper.treeToValue(customerNode, Customer.class);
System.out.println(event.toString());
}
}
}
}
For a single CUstomer
this would produce the following result:
Customer(isA=Customer, name=Superman, age=2013)
For the same code now if I provide the CustomerDocument
(the first JSON) then it would not work and fail with error:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "com.fasterxml.jackson.core.JsonParser.getText()" is null
at stackover.JacksonMain.main(JacksonMain.java:32)
I know this issue is happening because of the line
final JsonNode jsonNode = jsonParser.readValueAsTree();
Can someone please explain how to make the code work for both the type of JSON customerDocument
and just single Customer
using Jackson? I just want to differentiate whether incoming JSON is customerDocument
or single Customer
. Any help would be really appreciated.
additional classes
. However, it's fine if there is a need to create an interface
to achieve this.CustomerList
can be very huge so I am reading one by one so it does not make much memory. hence I do not have the CustomerDocument
class with List<Customer>
rather I am looking over it and mapping one by one.Well you can use Jackson sub type to de-serialize between Customer
and CustomerDocument
.
Something like following,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"isA\":\"CustomerDocument\",\"customerList\":[{\"isA\":\"Customer\",\"name\":\"Batman\",\"age\":\"2008\"}]}";
// String s = "{\"isA\":\"Customer\",\"name\":\"Superman\",\"age\":\"2013\"}";
ObjectMapper om = new ObjectMapper();
BaseResponse baseResponse = om.readValue(s, BaseResponse.class);
if (baseResponse instanceof CustomerDocument) {
CustomerDocument cd = (CustomerDocument) baseResponse;
System.out.println("Inside If..");
cd.getCustomerList().forEach(System.out::println);
} else if (baseResponse instanceof Customer) {
System.out.println("Inside Else If..");
Customer cs = (Customer) baseResponse;
System.out.println(cs);;
}
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
@JsonSubTypes.Type(value = Customer.class, name = "Customer"),
@JsonSubTypes.Type(value = CustomerDocument.class, name = "CustomerDocument")})
interface BaseResponse {}
@Getter
@Setter
@ToString
class Customer implements BaseResponse{
private String isA;
private String name;
private String age;
}
@Getter
@Setter
@ToString
class CustomerDocument implements BaseResponse{
private String isA;
private List<Customer> customerList;
}
PS - Uncomment the string in main
method to illustrate the other case.
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"isA\":\"CustomerDocument\",\"customerList\":[{\"isA\":\"Customer\",\"name\":\"Batman\",\"age\":\"2008\"},{\"isA\":\"Customer B\",\"name\":\"Superman\",\"age\":\"2013\"}]}";
// String s = "{\"isA\":\"Customer\",\"name\":\"Superman\",\"age\":\"2013\"}";
ObjectMapper om = new ObjectMapper();
JsonNode node = om.readTree(s);
String type = node.get("isA").asText();
if (type.equals("Customer")) {
Customer c = om.readValue(s, Customer.class);
System.out.println(c);
} else if (type.equals("CustomerDocument")) {
JsonNode nextNode = node.path("customerList");
List<Customer> cl = om.convertValue(nextNode, new TypeReference<List<Customer>>() {});
cl.forEach(System.out::println);
}
}
}
@Getter
@Setter
@ToString
class Customer {
private String isA;
private String name;
private String age;
}