Home > Software engineering >  Consuming external API and converting from JSON string to bean doesn't initialize properties. N
Consuming external API and converting from JSON string to bean doesn't initialize properties. N

Time:03-24

This is the response I get from the API.

{"get":"statistics","parameters":{"country":"romania"},"errors":[],"results":1,"response":[{"continent":"Europe","country":"Romania","population":19016885,"cases":{"new":" 4521","active":156487,"critical":431,"recovered":2606660,"1M_pop":"148707","total":2827936},"deaths":{"new":" 35","1M_pop":"3407","total":64789},"tests":{"1M_pop":"1149381","total":21857638},"day":"2022-03-24","time":"2022-03-24T07:30:04 00:00"}]}

@RestController
public class CovidTrackerRestController {
    
    @GetMapping("/hello")
    public String showCovidInformation() {
        
        // connect to a covid database
                        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://covid-193.p.rapidapi.com/statistics?country=romania"))
                .header("X-RapidAPI-Host", "covid-193.p.rapidapi.com")
                .header("X-RapidAPI-Key", "mykey")
                .method("GET", HttpRequest.BodyPublishers.noBody())
                .build();
        HttpResponse<String> response = null;
        
        try {
            response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException | InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
                        
        // get the information
        String responseString = response.body();
        System.out.println(responseString);
        
        Response romaniaData = null;
        
        try {
            romaniaData = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                            .readValue(responseString, Response.class);
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            
        
        // format the information
        System.out.println(romaniaData);
                
        
        // send the information to html page
        return "/tracker";
    }
}

And this is my Bean class which is annotated with @Bean in the configurator class alonside the RestTemplate bean. Other properties such as Cases, Deaths etc are configured same as Response class except being declared as @Bean in the configurator because from what I know once I declare a class @Bean then other references contained automatically become beans as well.

@JsonIgnoreProperties(ignoreUnknown = true)
public class Response {

    @JsonProperty("country")
    private String country;
    
    @JsonProperty("cases")
    private Cases cases;
    
    @JsonProperty("deaths")
    private Deaths deaths;
    
    @JsonProperty("day")
    private String day;
    
    @JsonProperty("time")
    private String time;
    
    @JsonProperty("test")
    private Tests tests;
    
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }

CodePudding user response:

Your java class needs to be exact representation of received json. Let's call it Wrapper:

public class Wrapper {

    @JsonProperty("response")
    private List<Response> responses;

    public List<Response> getResponses() {
        return this.responses;
    }

    public void setResponses(List<Response> responses) {
        this.responses = responses;
    }

    @Override
    public String toString() {
        return "Wrapper{"  
                "responses="   responses  
                '}';
    }
}

I am omiting some properties - get, results, etc. It looks you don't need them. Then deserialization will look like this:

Wrapper data = null;
try {
    data = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .readValue("json", Wrapper.class);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
System.out.println(data);

Few notes:

  1. If json property name matches field name in class, there is no need for @JsonProperty
  2. For tests field annotation should be - @JsonProperty("tests"). Property is tests, not test

If you really want to throw the rest of the data, and only need response property, then you need to write custom deserializer and work the json tree. You can see how to do it in my answer here, or this guide, for example. Like this you can parse the response json to your class, even if their structures do not match.

CodePudding user response:

Yes, your class should be like this:

public class ResponseWrapper {
  public Response parameters;

  public setParameters(Response parameters) {
    this.parameters - parameters;
  }
  public Response getParameters() {
    return parameters;
  }
}

And class Response is your class as you published it. Your class have to have the same structure as JSON

  • Related