简体   繁体   中英

Parsing a JSON array of values as POJOs

I need to process responses from an external SAP service which returns data as an array of arrays like this:

{
    "INFO":"",
    "SERVICE_NAME":[["PARAM1","PARAM2","PARAM3","PARAM4"]],
    "RESULTS":[
        ["object1fieldValue1","object1fieldValue2"],
        ["object2fieldValue1","object2fieldValue2"]
    ]
}

I would like to process the RESULTS further as a list of DTOs like this:

public class ResultItemDto {
    private String field1;
    private String field2;
}

instead of working with every object as a list of Strings and possibly mapping it manually to DTOs.

What would be the right, concise "Jackson way" of mapping such JSON directly to POJOs?

You can annotate a class with @JsonFormat to indicate that it will appear as an array in JSON:

@JsonFormat(shape = JsonFormat.Shape.ARRAY)
class ResultItemDto {
    private String field1;
    private String field2;
}

To verify the above I created a wrapper class for the full object:

class ResultWrapper {
    private List<ResultItemDto> results;
}

And deserialize like this:

ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);

ResultWrapper result = mapper.readValue(json, ResultWrapper.class);

Where ACCEPT_CASE_INSENSITIVE_PROPERTIES is used because your JSON has fields with upper case names.

Documentation for @JsonFormat and pertinent shapes

The "right" way is very subjective, but since you were curious about a concise way, your options are limited given that the fields you want access to are contained within arrays of arrays. Given this structure, mapping directly via annotations and without using custom deserializers is going to be out of the question. Your two options for custom parsing are the databind api and the streaming api. The databind API tends to be a bit easier to read and write, so we'll use that to fulfill your requirements of conciseness

Below is some code that uses an ObjectReader to parse the json tree and then safely uses JsonNode's path , isArrayNode , and asText methods to pluck out the values of those inner arrays if they are where expected, and will not result in an exception if the json you receive is in a different structure. This assumes you want to throw away the rest of the JSON body, as your question only discusses mapping specifically to the ResultItemDto.

package foo.bar;


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.node.ArrayNode;

import java.util.ArrayList;
import java.util.List;

public class Test {

    static final String JSON = "{\n" +
                                       "    \"INFO\":\"\",\n" +
                                       "    \"SERVICE_NAME\":[[\"PARAM1\",\"PARAM2\",\"PARAM3\",\"PARAM4\"]],\n" +
                                       "    \"RESULTS\":[\n" +
                                       "        [\"object1fieldValue1\",\"object1fieldValue2\"],\n" +
                                       "        [\"object2fieldValue1\",\"object2fieldValue2\"]\n" +
                                       "    ]\n" +
                                       "}";

    static class ResultItemDto {
        private String field1;
        private String field2;
    }


    public static void main(String[] args) throws Exception {
        List<ResultItemDto> list = new ArrayList<ResultItemDto>();

        ObjectReader reader = new ObjectMapper().reader();
        JsonNode resultsNode = reader.readTree(JSON).path("RESULTS");
        if (resultsNode.isArray()) {
            ArrayNode arrNode = (ArrayNode) resultsNode;
            arrNode.elements().forEachRemaining(elem -> {
                ResultItemDto dto = new ResultItemDto()
                dto.field1 = elem.path(0).asText();
                dto.field2 = elem.path(1).asText();
                list.add(dto);
            });
        }
    }
}

If you wanted to annotate your DTO class, the above code could be used inside of a custom deserializer once you've annotated the dto with @JsonDeserialize and can call the readTree method on the JsonParser you must use when deserializing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM