2

I am trying to find a clean way of parsing nested properties from a payload from an API.

Here is a rough generalisation of the JSON payload:

{
  "root": {
    "data": {
      "value": [
        {
          "user": {
            "id": "1",
            "name": {
              "first": "x",
              "last": "y"
            }
          }
        }
      ]
    }
  }
}

My goal is to have an array of User objects which have firstName and lastName fields.

Does anyone know a good way to parse this cleanly?

Right now I am trying to create a Wrapper class and within that have static inner classes for data, value, user etc. but this seems like a messy way of doing this just to read the array of first/last properties.

I am using restTemplate.exchange() to call the endpoint.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
sam
  • 2,469
  • 8
  • 37
  • 57
  • If you have time, could you give a bit more detail? Are you just trying to get the first and last name values and ignoring the rest? Do you want to ignore the rest of the root/data object structure in your application code? – hooknc Sep 17 '19 at 17:19
  • I want an array of `User` objects with a first and last name. I.e. In the JSON above, I want to only extract the `first` and `last` properties. Everything else should be ignored. – sam Sep 17 '19 at 17:22
  • I see. There might be a way to do this with Jackson, but my guess is that you're going to have to deserialize the entire json string as is and get the full object graph from root -> data - value -> etc... Then using some code to map that full object graph into the data model that you want. From my observations this is a common process and one of the common side effects of using remote APIs. – hooknc Sep 17 '19 at 17:28

2 Answers2

3

JsonPath library allows one to select only required fields and thn you can use Jackson to convert raw data to POJO class. Example solution could look as follows:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.jayway.jsonpath.JsonPath;

import java.io.File;
import java.util.List;
import java.util.Map;

public class JsonPathApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        List<Map> nodes = JsonPath.parse(jsonFile).read("$..value[*].user.name");

        ObjectMapper mapper = new ObjectMapper();
        CollectionType usersType = mapper.getTypeFactory().constructCollectionType(List.class, User.class);
        List<User> users = mapper.convertValue(nodes, usersType);
        System.out.println(users);
    }
}

class User {

    @JsonProperty("first")
    private String firstName;

    @JsonProperty("last")
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "User{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}

Above code prints:

[User{firstName='x', lastName='y'}]
Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
0

Another simple way would be with JSON.simple library:

JSONParser jsonParser = new JSONParser();
        //Read JSON file
        Object obj = jsonParser.parse(reader);

        JSONObject jObj = (JSONObject) obj;

        JSONObject root = (JSONObject)jObj.get("root");
        JSONObject data = (JSONObject) root.get("data");
        JSONArray value =  (JSONArray) data.get("value");
        JSONObject array = (JSONObject) value.get(0);
        JSONObject user = (JSONObject) array.get("user");
        JSONObject name = (JSONObject) user.get("name");

        String lastName = (String) name.get("last");
        String firstName = (String) name.get("first");

        System.out.println(lastName + " " + firstName);
Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
Alexandr Kovalenko
  • 934
  • 1
  • 12
  • 13