Issue
I am trying to create a Spring MVC web application that checks the current weather by calling an external API from "https://api.weather.gov/points/{latitude},{longitude}". The latitude and longitude needs to be an user input which you take from an html form, pass it onto your controller and then use that latitude and longitude while you make the external call. The json from the api call contains grid points which again is necessary to make another external API call "https://api.weather.gov/gridpoints/{gridX},{gridY}/forecast" and then extract results and show it on the front end.
My html form looks something like this.(homepage.html)
<div class="call-logs-info" id="call-logs-id">
<div class="logs-container" id="logs-container-id">
<form id="my-form" action="/getLatitude" method="post">
<div class="date-content">
<h1>Weather Checker</h1>
<div class="columns">
<div class="col col-1">
<p>Latitude:</p>
<input type="text" class="inputbox" name="latitude" id="lat" />
</div>
<div class="col col-2">
<p>Longitude:</p>
<input type="text" class="inputbox" name="longitude" id="lon" />
</div>
</div>
<div class="columns">
<div class="col col-2">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</div>
</div>
</form>
</div>
</div>
I have a model class that looks something like this.(Paths.java)
@Data
public class Paths {
@SerializedName("latitude")
public double latitude;
@SerializedName("longitude")
public double longitude;
}
My @Controller class from where I have called the @RestController class to call the external API looks something like this.I have extracted latitude and longitude from this class using @ModelAttribute and tried passing it to the getGridPoints() method. (APIFetchController.java)
@Log4j2
@Controller
public class APIFetcherController {
@Autowired
private ExternalApiFetchController externalApiFetchController;
@GetMapping("/home")
public String showHomePage(){
return "homepage";
}
@PostMapping("/getLatitude")
public void formSubmit(@ModelAttribute Paths paths){
Double latitude = paths.getLatitude();
Double longitude = paths.getLongitude();
log.info("Latitude: {}", latitude);
log.info("Longitude: {}", longitude);
externalApiFetchController.getGridPoints(latitude,longitude);
}
}
Last but not the least, my @RestController class from where I have tried calling the external API looks something like this. I have tried using restTempelate in order to do that.(ExternalApiFetchController.java)
@Log4j2
@RestController
public class ExternalApiFetchController {
@GetMapping("/grids")
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
public List<Object> getGridPoints(Double latitude, Double longitude) {
log.info("Lat :{}", latitude);
log.info("Long :{}", longitude);
String url = "https://api.weather.gov/points/"+latitude + "," +longitude;
log.info("URL: {}", url);
RestTemplate restTemplate = new RestTemplate();
Object[] grids = restTemplate.getForObject(url, Object[].class);
return Arrays.asList(grids);
}
For some reason, this throws an error while trying to check on Postman which says
"Cannot deserialize value of type [Ljava.lang.Object;
from Object value (token JsonToken.START_OBJECT
)" and some other error."
The latitude and longitude value after hitting the API from Postman becomes null for some reason when I check the logs.
I know this can be done through soley frontEnd but my task at hand is to do it through springMVC and I don't know if this is the right way of doing the task. Any help would be so precious right now.
Solution
The weather API return an json object as the result. You are telling spring to convert it into an array of Objects. The error points out that you cannot do so as it is expecting the json response to be an array of json objects .
Replace
Object[] grids = restTemplate.getForObject(url, Object[].class);
with
Object grid = restTemplate.getForObject(url, Object.class);
(https://www.weather.gov/documentation/services-web-api#/default/point)
Answered By - Jayesh
Answer Checked By - Candace Johnson (JavaFixing Volunteer)