Jackson deserialization using JsonParser distinguish between the direct object and objects within array

21 views
Skip to first unread message

Aravinda Baliga B

unread,
Sep 2, 2021, 12:24:11 AM9/2/21
to jackson-user
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.

Aravinda Baliga B

unread,
Sep 3, 2021, 2:43:12 AM9/3/21
to jackson-user
Following worked for me:

BaseResponse interface:
```
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Customer.class, name = "Customer")})
public interface BaseResponse {
}
```

Customer class:
```
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer implements BaseResponse {
    private String isA;
    private String name;
    private String age;
}
```

```
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();

        try {
            BaseResponse baseResponse = objectMapper.readValue(jsonParser, BaseResponse.class);
            System.out.println("SINGLE EVENT INPUT");
            System.out.println(baseResponse.toString());
        } catch (Exception e) {
            System.out.println("LIST OF CUSTOMER INPUT");
            //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, BaseResponse.class);
                System.out.println(event.toString());
            }
        }

    }
}
```
Reply all
Reply to author
Forward
0 new messages