Search code examples
javarestjacksonjava-http-client

Java Jackson how to parse large REST API JSON?


I'm trying to get info from https://prices.runescape.wiki/api/v1/osrs/latest

 HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .GET()
                    .uri(URI.create("https://prices.runescape.wiki/api/v1/osrs/latest"))
                    .build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            ObjectMapper mapper = new ObjectMapper();
            JsonNode json = mapper.readTree(response.body());
            JsonNode data = json.get("data");
            List<Data> items = new ArrayList<>();
            data.forEach(item -> {
                Data d = new Data(
                        item.get("high").asInt(),
                        item.get("highTime").asInt(),
                        item.get("low").asInt(),
                        item.get("lowTime").asInt()
                );
                items.add(d);

            });

Problem is the object is the itemID. So if I want the item id 6. I can't do it since it's not an attribute of the object.

"2":{"high":164,"highTime":1672078170,"low":160,"lowTime":1672078164} ``

"2" is the itemID and the object.

Below is when I loop through the arraylist and print it out

Data{high=157, highTime=1672071161, low=150, lowTime=1672071151}
Data{high=187987, highTime=1672071066, low=182005, lowTime=1672070881}
Data{high=189903, highTime=1672071052, low=186820, lowTime=1672070884}
Data{high=190000, highTime=1672070957, low=184882, lowTime=1672070984}

Solution

  • Your JSON has the following structure:

    {
       "data":{
          "2":{ ... },
          "6":{ ... },
          "8":{ ... },
          "10":{ ... },
          "12":{ ... }
          ...
       }
    }
    

    And property "data" is associated not with an object, or a List<Data>, but with a Map<Integer,Data> (or Map<String,Data>.

    There are several ways how you can parse:

    • You can define an object with a single property Map<Integer,Data> data.
    public static class DataWrapper {
        private Map<Integer, Data1> data;
        
        // getters, setters
    }
    

    Usage example:

    String json = """
                {
                   "data":{ ... }
                }
                """;
    
    ObjectMapper mapper12 = new ObjectMapper();
            
    DataWrapper dataWrapper = mapper12.readValue(json12, DataWrapper.class);
    Map<Integer, Data1> dataMap = dataWrapper.getData();
        
    System.out.println("item 6: " + dataMap.get(6));
    
    • Another approach would be to create a JsonNode by parsing the given JSON, access the node mapped to the property "data" and parse it as a Map using ObjectReader and its method ObjectReader.readValue(). To generate ObjectReader we can make use of the method ObjectMapper.readerFor() which expects a TypeReference.

    That's how the second approach might be implemented:

    String json = """
                {
                   "data":{ ... }
                }
                """;
            
    ObjectMapper mapper = new ObjectMapper();
    JsonNode tree = mapper.readTree(json);
            
    ObjectReader reader = mapper.readerFor(new TypeReference<Map<Integer, Data>>() {});
    Map<Integer, Data> itemsMap = reader.readValue(tree.get("data"));
        
    System.out.println("item 6: " + itemsMap.get(6));
    

    Output:

    item 6: Data{high=195500, highTime=1672079035, low=182009, lowTime=1672078518}