Parsing HttpResponse into customized object - java

I'm using some weather api that returns actual forecast for given city.
I would like to parse HtmlResponse into my object which looks like this:
public class Weather {
String countryCode;
String city;
double temperature;
double sensedTemperature;
int humidity;
int windSpeed; // meter/sec
int windDirection;
int pressure;
int weatherDescription;
}
Method that does whole operations:
#GetMapping("/weather")
public void getCurrentWeather(#RequestParam String city, #RequestParam(required = false) String lang,
#RequestParam(required = false) String units)
throws UnirestException, UnsupportedEncodingException {
//Params
String query = getQueryAccordingToGivenParameters(city, lang, units);
HttpResponse<JsonNode> response = Unirest.get(HOST + "?" + query)
.header("x-rapidapi-host", X_RAPID_HOST)
.header("x-rapidapi-key", X_RAPIDAPI_KEY)
.asJson();
System.out.println(response.getBody());
}
Right now it prints response in command line, but I would like to convert it to Weather object.
JSON looks like this:
{
"visibility": 10000,
"timezone": 0,
"main": {
"temp": 7.21,
"temp_min": 5.56,
"humidity": 81,
"pressure": 1029,
"feels_like": 4.87,
"temp_max": 9
},
"clouds": {
"all": 75
},
"sys": {
"country": "GB",
"sunrise": 1577433953,
"sunset": 1577462200,
"id": 1414,
"type": 1
},
"dt": 1577444681,
"coord": {
"lon": -0.13,
"lat": 51.51
},
"weather": [
{
"icon": "04d",
"description": "broken clouds",
"main": "Clouds",
"id": 803
}
],
"name": "London",
"cod": 200,
"id": 2643743,
"base": "stations",
"wind": {
"speed": 1.5
}
}
I need
"name" - london,
"wind" - speed - 1.5
"weather" - description - "broken clouds"
"main" - temp - 7.21
"main" - humidity - 81
and some others but these are just examples.
I tried to use JSONObject and get wind speed like this:
JSONObject object = new JSONObject(response);
JSONObject windObject = new JSONObject(object);
String wind = windObject.getString("wind");
System.out.println(wind);
but I got org.json.JSONException: JSONObject["wind"] not found.
Could you tell me how to fetch the speed of the wind and how to fetch description weather? The rest I should do by my own.
//within Andreas answer code looks like this:
package weatherapp;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import org.json.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
#RestController
public class WeatherController {
public static final String X_RAPID_HOST = "community-open-weather-map.p.rapidapi.com";
public static final String X_RAPIDAPI_KEY = "...";
public static final String CHARSET = "UTF-8";
public static final String HOST = "https://community-open-weather-map.p.rapidapi.com/weather";
//{city}/{lang}/{units]
#GetMapping("/weather")
public void getCurrentWeather(#RequestParam String city, #RequestParam(required = false) String lang,
#RequestParam(required = false) String units)
throws UnirestException, UnsupportedEncodingException {
//Params
String query = getQueryAccordingToGivenParameters(city, lang, units);
HttpResponse<JsonNode> response = Unirest.get(HOST + "?" + query)
.header("x-rapidapi-host", X_RAPID_HOST)
.header("x-rapidapi-key", X_RAPIDAPI_KEY)
.asJson();
JSONObject root = new JSONObject(response);
JSONObject wind = root.getJSONObject("wind");
double windSpeed = wind.getDouble("speed");
System.out.println(windSpeed);
}
...
}
but it throws:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.json.JSONException: JSONObject["wind"] not found.] with root cause
org.json.JSONException: JSONObject["wind"] not found.
at org.json.JSONObject.get(JSONObject.java:471) ~[json-20160212.jar:na]
at org.json.JSONObject.getJSONObject(JSONObject.java:636) ~[json-20160212.jar:na]
at weatherapp.WeatherController.getCurrentWeather(WeatherController.java:42) ~[classes/:na]
...
After debugging root looks like this:

UPDATED
It really helps when you read the documentation of the API you're using. Providing relevant links to javadoc below!!!
Since response is an HttpResponse<JsonNode> object, you need to call getBody() to get the body of the response, call getObject() on the already parsed JsonNode object to get the root JSONObject object, call getJSONObject to get the nested wind object, and finally call getDouble to get the speed value.
JsonNode rootNode = response.getBody();
JSONObject rootObj = rootNode.getObject();
JSONObject windObj = rootObj.getJSONObject("wind");
double windSpeed = windObj.getDouble("speed");

You can fetch wind speed with:
JSONObject json = new JSONObject(response);
JSONObject wind = json.getJSONObject("wind");
double speed = wind.getDouble("speed");
And the weather description as follows:
JSONObject json = new JSONObject(response);
JSONObject weather = json.getJSONArray("weather").getJSONObject(0);
String description = weather.getString("description");

your json output shows that "wind" key is an object and you are trying to get wind as string.
Use this
JSONObject object = new JSONObject(response);
JSONObject windObject = object.getJSONObject("wind");
String speed = windObject.getString("speed");
System.out.println(speed);

I needed to do this:
HttpResponse<JsonNode> response = Unirest.get(HOST + "?" + query)
.header("x-rapidapi-host", X_RAPID_HOST)
.header("x-rapidapi-key", X_RAPIDAPI_KEY)
.asJson();
JSONObject z = response.getBody().getObject();
JSONObject zz = z.getJSONObject("wind");
double wind = zz.getDouble("speed");

Related

How to convert JSON which has array to Java objects

Unable to convert a JSON string which has few elements and an array inside it.
On UI I need to feed the array to bootstrap table.
JSON string:
{
"IsOperationSuccessful": true,
"IsResult": true,
"StatusCode": "OK",
"Response": [{
"PlaylistCreatedBy": "XYZ",
"PlaylistCreatedOn": "10/10/2019 14:10",
"PlaylistDisplayTitle": "blah",
"PlaylistId": 101,
"PlaylistScheduledReleaseTime": "10/10/2019 14:10"
}, {
"PlaylistCreatedBy": "HHJK",
"PlaylistCreatedOn": "10/10/2019 14:10",
"PlaylistDisplayTitle": "blah blah",
"PlaylistId": 102,
"PlaylistScheduledReleaseTime": "10/10/2019 14:10"
}, {
"PlaylistCreatedBy": "HJHJ",
"PlaylistCreatedOn": "10/10/2019 14:10",
"PlaylistDisplayTitle": "UIUI",
"PlaylistId": 103,
"PlaylistScheduledReleaseTime": "10/10/2019 14:10"
}, {
"PlaylistCreatedBy": "KJK",
"PlaylistCreatedOn": "10/10/2019 14:10",
"PlaylistDisplayTitle": "kkj",
"PlaylistId": 104,
"PlaylistScheduledReleaseTime": "10/10/2019 14:10"
}],
"Total": 4
}
The code that I have added so far:
PreRecordedCall morningCallResponse = new PreRecordedCall();
JSONArray playListinfo = null;
String testResponse = "//Json Goes here "
JSONObject finalJson = new JSONObject();
finalJson.put("testResponse", testResponse);
Gson gson = new Gson();
morningCallResponse = gson.fromJson(testResponse,
PreRecordedCall.class);
playListinfo = morningCallResponse.getPreRecordplayListInformation();
One way is to create 2 POJOs (says Response and PreRecordedCall) as follows.
The Response is for the JSON array with key "Response" and the PreRecordedCall is for the whole JSON object. The key is to use List<Response> to store JSON array.
BTW, to follow the naming rule for variables in POJOs with lowerCamelCase, I used #SerializedName for object name mapping.
Class Response
static class Response {
#SerializedName("PlaylistCreatedBy")
private String playlistCreatedBy;
#SerializedName("PlaylistCreatedOn")
private String playlistCreatedOn;
#SerializedName("PlaylistDisplayTitle")
private String playlistDisplayTitle;
#SerializedName("PlaylistId")
private int playlistId;
#SerializedName("PlaylistScheduledReleaseTime")
private String playlistScheduledReleaseTime;
//general getters and setters
}
Class PreRecordedCall
static class PreRecordedCall {
#SerializedName("IsOperationSuccessful")
private Boolean isOperationSuccessful;
#SerializedName("IsResult")
private Boolean isResult;
#SerializedName("StatusCode")
private String statusCode;
#SerializedName("Response")
private List<Response> response;
#SerializedName("Total")
private int total;
//general getters and setters
}
Then you can simply convert the JSON string to pre-defined POJOs as follows:
Gson gson = new Gson();
PreRecordedCall preRecordedCall = gson.fromJson(testResponse, PreRecordedCall.class);
System.out.println(preRecordedCall.getResponse().size());
Console output
4
And if you are using Jackson as your JSON parser, change #SerializedName to #JsonProperty and you can get the same result by following code snippet.
ObjectMapper mapper = new ObjectMapper();
PreRecordedCall preRecordedCall = mapper.readValue(testResponse, PreRecordedCall.class);
System.out.println(preRecordedCall.getResponse().size());
Check if the below code works for you:
String finalJson= "//Json Goes here ";
ObjectMapper objectMapper = new ObjectMapper();
try {
PreRecordedCall morningCallResponse = objectMapper.readValue(json, PreRecordedCall.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}

How to send String[] and String values on the same API's body using Rest Assured?

I'm trying to create a Test Automation for a POST API using Rest-Assured and Java.
This API has the following body as Application/JSON:
{
"billing_address": {
"region": "Sao Paulo",
"region_id": 123,
"country_id": "BR",
"street": ["Av Paulista"],
"postcode": "10577",
"city": "SAO PAULO",
"telephone": "555-555-1111",
"company": "Test test",
"firstname": "Fernando",
"lastname": "Alves",
"document": "0123444064"
}
}
In my code, I developed the following method to send this values and the request:
public void criarPF (String srtAmbiente, String srtAPI, String srtToken, String srtRegion, String srtRegionID, String srtCountryID, String srtStreet,
String srtPostcode, String srtCity, String srtTelephone, String srtCompany, String srtFirstname, String srtLastname, String srtDocument) {
String uriBase = srtAmbiente;
String Api = srtAPI;
Map<String, String> addressContent = new HashMap<String,String>();
addressContent.put("region", srtRegion);
addressContent.put("region_id", srtRegionID);
addressContent.put("country_id", srtCountryID);
addressContent.put("street", srtStreet);
addressContent.put("postcode", srtPostcode);
addressContent.put("city", srtCity);
addressContent.put("telephone", srtTelephone);
addressContent.put("company", srtCompany);
addressContent.put("firstname", srtFirstname);
addressContent.put("lastname", srtLastname);
addressContent.put("document", srtDocument);
Map<String, Object> postContent = new HashMap<String,Object>();
postContent.put("billing_address", addressContent);
request = RestAssured.given().contentType(ContentType.JSON)
.header("Authorization", "Bearer "+srtToken).with().body(postContent);
I received an error messenger that "street" isn't a String, it's a String[].
So, how can I send a String and String[] values in the same API's body?
You are setting street to a string value. Just change it like this:
Map<String, Object> addressContent = new HashMap<>();
addressContent.put("region", srtRegion);
addressContent.put("region_id", srtRegionID);
addressContent.put("country_id", srtCountryID);
// Here wrap strStreet in a List
addressContent.put("street", Arrays.asList(srtStreet));
addressContent.put("postcode", srtPostcode);
addressContent.put("city", srtCity);
addressContent.put("telephone", srtTelephone);
addressContent.put("company", srtCompany);
addressContent.put("firstname", srtFirstname);
addressContent.put("lastname", srtLastname);
addressContent.put("document", srtDocument);

Spring ResponsEntity body contains extra json with timestamp, status and more

We have an REST endpoint that will add a new empty ingredient to an existing meal:
#RequestMapping(value = "/add", method = RequestMethod.PUT, consumes = "application/json;charset=UTF-8")
public ResponseEntity<Object> add(#RequestBody final Meal meal) throws URISyntaxException
{
Optional<Meal> optionalMeal = mealRepository.findById(meal.getId());
if (!optionalMeal.isPresent()) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(MessageUtil.parse(MSG_404_MEAL, meal.getId() + ""));
}
Ingredient ingredient = new Ingredient();
ingredient.setMeal(optionalMeal.get());
ingredientRepository.saveAndFlush(ingredient);
ResponseEntity re = ResponseEntity
.created(RequestUtil.getResourceURI(ingredient.getId()))
.body(ingredient);
return re;
}
Ingredient is an entity class with some fields:
public class Ingredient implements Serializable
{
#Id
private Integer id;
private Meal meal;
private Grocery grocery;
private Float amount;
...
}
RequestUtil takes care of creating the URI where the newly created resource is to be found:
public class RequestUtil
{
public static URI getResourceURI(int id) throws URISyntaxException
{
final String url = RequestUtil.getCurrentRequest().getRequestURL().toString();
final String req = RequestUtil.omitLast(url);
return new URI(req + "get/" + id);
}
public static HttpServletRequest getCurrentRequest()
{
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
public static String omitLast(final String url) {
return url.substring(0, url.lastIndexOf("/") + 1);
}
}
The http status code and resource URI end up correctly in the response headers, but the body contains two JSONs:
{
"id": 407,
"meal": {
"id": 99,
"name": "New Meal",
"active": true
},
"grocery": null,
"amount": null,
"bought": false
} {
"timestamp": "2018-08-29T19:25:31.466+0000",
"status": 201,
"error": "Created",
"message": "No message available",
"path": "/ingredient/add"
}
Our javascript code does not expect this extra data and fails with
SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 114 of the JSON data
Using a debugger, we can see that by the time the code reaches the return statement in add(), the ResponseEntity does not contain this extra data. Can someone explain where it comes from, and how we stop it from polluting the response?
Thanks for any help!

MappingJackson2HttpMessageConverter Mistake on Converting Complex Object

i have Class Response that return to client for every request from client my Response is:
public class Response<T> extends Request{
private ResponseType responseType;
private String message;
private ArrayList<T> result;
private int activationCode;
.
.
.
}
in my server side i have method that return Response that contains results with arraylist of InstagramUser
my method:
public Response getUserByUserName(#RequestBody List<Request> requests){
.
.
.
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
try {
String searchResponse = restTemplate.getForObject(uri, String.class);
JSONObject rawObject = new JSONObject(searchResponse);
JSONArray searchResults = rawObject.getJSONArray("data");
ArrayList<InstagramUser> users = new ArrayList<InstagramUser>();
for (int i = 0; i < searchResults.length(); i++) {
JSONObject result = searchResults.getJSONObject(i);
users.add(new InstagramUser(result, AccessToken.getTokenString()));
}
response = new Response(requests.get(0).getUserId(), ResponseType.success, "users find successfully on: " + new Date().toString());
response.setResult(users);
.
.
.
return response;
}
and my InstagramUser:
public class InstagramUser extends InstagramModel {
protected long id;
protected String userName;
protected String fullName;
protected String profilePictureURI;
protected String bio;
protected String website;
protected int mediaCount = -1;
protected int followerCount = -1;
protected int followingCount = -1;
...
}
but in client side when i get Response from server my results is ArrayList of LinkedHashMap insted of ArrayList of InstagramUser:
restTemplate.postForObject(URL + conditions, params,Response.class);
and this is my json response from server for calling this method:
{
"id": 6151638910251304448,
"userId": 2,
"instagramId": null,
"searchId": null,
"mediaId": null,
"phoneNumber": null,
"name": null,
"date": 1466665008687,
"responseType": "success",
"message": "users find successfully on: Thu Jun 23 11:26:48 IRDT 2016",
"result": [
{
"id": 110000004535,
"userName": "______etabdar",
"fullName": "________dar",
"profilePictureURI": "https://igcdn-photos-h-a.akamaihd.net/hphotos-ak-xap1/t51.2885-19/s150x150/13183XXXXXXXX0231_1584363729_a.jpg",
"bio": "🎓 XXXXX 90",
"website": "",
"mediaCount": -1,
"followerCount": -1,
"followingCount": -1
}
],
"activationCode": 0
}
how can i fix this?
you'll have to use parameterized type reference. Works only with rest template exchange methods.
List<YourObjectType>> res = template.exchange(
rootUrl,
HttpMethod.POST,
null,
new ParameterizedTypeReference<List<YourObjectType>>() {});
Adjust parameters based on your inputs.
It looks like
restTemplate.postForObject(URL + conditions, params,Response.class)
doesn't know the specific type this part of your servers response:
"result": [
{
"id": 110000004535,
"userName": "______etabdar",
"fullName": "________dar",
"profilePictureURI": "https://igcdn-photos-h-a.akamaihd.net/hphotos-ak-xap1/t51.2885-19/s150x150/13183XXXXXXXX0231_1584363729_a.jpg",
"bio": "🎓 XXXXX 90",
"website": "",
"mediaCount": -1,
"followerCount": -1,
"followingCount": -1
}
],
should be mapped to when assigning
private ArrayList<T> result;
of your Response class, therefore switching to a default type (probably LinkedHashMap).
Maybe the answer to the following SO-question is of any help:
Using Spring RestTemplate in generic method with generic parameter

Dynamic JSON Data Object in GSON

I'm having a bit of trouble with GSON reading my API response JSON.
My API data returns an object with a status code, message and a data object.
The problem that I have with GSON, is that I can't figure out how to work with it properly.
For example, my API response can look like this.
{
"code": 200,
"message": "",
"data": {
"auth_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"points": 42850
}
}
OR
{
"code": 200,
"message": "",
"data": {
"items": [
{
"title" : "value"
},
{
"title" : "value"
}
]
}
}
OR others
The first response, which is a login response would be a class LoginResponse.class
public class LoginResponse {
private String auth_token;
private int points;
public String getAuthToken(){
return auth_token;
}
public int getPoints(){
return points;
}
}
and I'd use
LoginResponse response = gson.fromJson(json, LoginResponse.class);
But, how can I create a class so I can access the status code, message and the data? (which could be any of the response classes that I have)
I've looked at TypeAdapterFactory and JsonDeserializer, but couldn't get my head around it.
So, if anyone can find a SO answer that answers this question, that'd be great because I couldn't find one, or if you know how to do just this.
You could have code and message as normal, and then data could be a Map<String, Object> that you would have to interpret at runtime depending on the code or whatever you use to differentiate how the response should look.
You can solve it by doing this:
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
JsonParser parser = new JsonParser();
JsonObject rootObejct = parser.parse(json).getAsJsonObject();
JsonElement dataElement = rootObejct.get("data");
LoginResponse response = gson.fromJson(dataElement, LoginResponse.class);

Categories

Resources