Parsing JSON array with Array of objects inside with Retrofit - java

i have this fragment of a json response from an endpoint in where i need to get some news, but is kinda returning me wrong info or maybe i'm doing this wrong:
{
"data": [
{
"alignImages": "left",
"data": [
{
"content": "Despus de dos aos de la operacin y una intensiva terapia, jvenes que no podan mover sus miembros inferiores y superiores ahora pueden comer, cepi",
"id": 179,
"title": "Ciruga que reconecta los nervios le devolvi a 13 tetrapljicos la movilidad en",
"url_detail": "https://www.elespectador.com/noticias/salud/cirugia-que-reconecta-los-nervios-le-devolvio-13-tetraplejicos-la-movilidad-en-brazos-y-codos-articulo-869",
"url_image": "https://storage.googleapis.com/sitidoctor-161813.appspot.com/images/noticia_2.png"
},
{
...
}
],
"imgHeader": "https://storage.googleapis.com/sitidoctor-161813.appspot.com/images/Noticias_Salud.png",
"titleHeader": "Noticias Salud"
}
],
"message": "success!",
"status": 200
}
This is the model i use to parse the response from the retrofit instance query:
public class NewsResponse {
#SerializedName("data")
#Expose
private List<New> data = null;
#SerializedName("message")
#Expose
private String message;
#SerializedName("status")
#Expose
private Integer status;
public List<New> getData() {
return data;
}
public void setData(List<New> data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
and i make the request using retrofit like this:
private void handleResponse(NewsResponse newsResponse) {
Log.e("response", newsResponse.getData().toString());
if (newsResponse.getData().size() > 0){
List<New> n = new ArrayList<>();
for (int i = 0; i < newsResponse.getData().size(); i++){
New nn = newsResponse.getData().get(i);
n.add(nn);
}
callback.onSuccess(n);
}
}
but i'm always getting the a single and empty value, is like is returning the inner data array as object but cant get data, i tried everything and got nothing, any ideas?
Reference imagen in debugger

Looking into your debugger image, Your newsResponse.getData().size() is 1. The only element in the list has id,title,content, uridetail as null. You are handling the response correctly. There is something wrong with your response. Please check if you are making the network call correctly

Related

How to consume Json from rest template and map for each element to another Json in Spring Boot

I am not able to map a consumed json to another json to each element by using REST template in Spring Boot
Controller code:
public List<Getrequests> getallrequests() {
List<Getrequests> list=Serviceobj.allrequestdata();
return list;
}
Service code:
public List<Getrequests> allrequestdata() {
String urlGETList = "http://localhost:8082/myapp/userinfo/getusertype/asd454";//get by id call
ResponseEntity<Usertype[]> responseEntity =resttemplateobj.getForEntity(urlGETList, Usertype[].class);
Usertype[]objects = responseEntity.getBody();
List results = admininfoDaoobj.getallrequestsdata();
//results.add(objects);if i un comment this line of code i am getting 1 output means it just add to the list only
return results;
}
Dao code:
public List<Getrequests> getallrequestsdata(){
String hql = "FROM Createrequest";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
Query<Createrequest> query = getSession().createQuery(hql,Createrequest.class);
List resultlist= query.getResultList();
return resultlist;
}
Getrequests pojo class:
public class Getrequests{
private String userid;
private String username;
private String userphoneno;
.......getters and setters.....
}
User type pojo class:
public class Usertype{
private String usertype;
private String useraddress;
.......getters and setters.....
}
1 output:
[
{
"userid":"asd454",
"username":"satya",
"userphoneno":"1234567890"
},
{
"userid":"asd455",
"username":"satya",
"userphoneno":"1234567890"
}
[
{
"usertype":"admin,agent",
"useraddress:"dsadasd,asdasdsa"
},
{
"usertype":"agent",
"useraddress:"asdasdsa"
},
]
]
2 output
If I comment resultlist.add(objects) in Service code means it's not added to the getrequest list then I am getting below output:
[
{
"userid":"asd454",
"username":"satya",
"userphoneno":"1234567890"
},
{
"userid":"asd455",
"username":"satya",
"userphoneno":"1234567890"
}
]
But I need to map for each userid, I need to display the usertype and user address like below output.
3 Output
[
{
"userid":"asd454",
[
{
"usertype":"admin,agent",
"useraddress:"dsadasd,asdasdsa"
} ]
"username":"satya",
"userphoneno":"1234567890"
},
{
"userid":"asd455",
[
{
"usertype":"agent",
"useraddress:"asdasdsa"
}
]
"username":"satya",
"userphoneno":"1234567890"
}
]
I also tried the rest template call in Dao then also not getting.
How can I map for each userid to display the usertype and user address like as above shown 3 output.
From your snippets, it looks like you want to generate a specific POJO signature.
There are plenty of tools which can give you a stub out of a sample json request.
From your example, i would guess that changing the Getrequests would do the trick,
like :
public class Getrequests{
private String userid;
private String username;
private String userphoneno;
private Usertype userType;
.......getters and setters.....
}
You might need to change it to a list reference as well, depending on your schema and your overall requirement.
EDIT
public class Getrequests{
private String userid;
private String username;
private String userphoneno;
private List<Usertype> userType;
}
If i make the reference a list, then the below samples are the output :
Single object:
{
"userid":"69",
"username":"asd",
"userphoneno":"09876",
"userType":[
{
"usertype":"01",
"useraddress":"1234"
}
]
}
As An array :
[
{
"userid":"94",
"username":"asd",
"userphoneno":"09876",
"userType":[
{
"usertype":"01",
"useraddress":"1234"
}
]
},
{
"userid":"53",
"username":"asd",
"userphoneno":"09876",
"userType":[
{
"usertype":"01",
"useraddress":"1234"
}
]
}
]

How to Parse only a portion of a web JSON in Java using RestTemplate?

I'm trying to get the first 5 articles from this API: https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21
My code works perfectly for now, but it parses all 10 articles of NewsAPI.
The code is:
public News parse() {
return restTemplate.getForObject
("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", News.class);
}
}
and the result is:
{
"totalResults": 10,
"articles": [
{
"source": {
"id": "bbc-news",
"name": "BBC News"
},
"author": "BBC News",
"title": "Measles returns to four European nations, WHO says",
"url": "http://www.bbc.co.uk/news/health-49507253"
},
etc......
Of course, i created the classes that describe Article, Source and News. News has a List of Article.
I just want to parse the first five articles and save them into a List. I know I have to use a For cycle, but how can i do that? I tried with this code:
public News parseFive() {
List<Article> articleList = null;
for(int i = 0; i<5; i++) {
articleList = Arrays.asList(
new Article(restTemplate.getForObject
("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Article.class)));
}
News news = new News();
news.setArticles(articleList);
return news;
}
The News class is:
public class News {
private int totalResults;
private List<Article> articles;
public News() {
}
public int getTotalResults() {
return totalResults;
}
public void setTotalResults(int totalResults) {
this.totalResults = totalResults;
}
public List<Article> getArticles() {
return articles;
}
public void setArticles() {
this.articles = articles;
}
}
and the result is:
{
"totalResults": 0,
"articles": [
{
"source": null,
"author": null,
"title": null,
"url": null
}
]
}
Where is the problem? Maybe because the first class who finds is not Article but is News? Thanks everyone for the effort.
When you are using RestTemplate.getForObject you are technically parsing the whole response: Spring reads all the bytes and uses JSON parser (Jackson) to create an object. Your for loop, which is covered later, only filters out elements past 5th. If you really want to parse only first 5 articles, you should consider using Jackson Streaming API. It is quiet problematically to use with RestTemplate, read this answer for more info.
Now let's try to fix your parseFive.
First, create a class to capture whole response:
public class Response {
private String status;
private Integer totalResults;
private List<Artice> articles;
// Getters & Setters
}
Now, get first five articles:
public News parseFive() {
final Response response = restTemplate
.getForObject("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Response.class);
final News news = new News();
news.setArticles(response.articles.stream().limit(5).collect(Collectors.toList());
return news;
}
You have not provided your News class, probably it is the same as response. Then, the code may look like:
public News parseFive() {
final News news = restTemplate
.getForObject("https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=19acc3a371d145ecb37a093f9985ea21", Response.class);
news.setArticles(news.articles.stream().limit(5).collect(Collectors.toList());
return news;
}

Firebase Android: how do I access params nested in data via RemoteMessage?

via this shape:
{
"to": "000",
"priority": "high",
"data": {
"title": "A Title",
"message": "A Message",
"link": {
"url": "http://www.espn.com",
"text": "ESPN",
}
}
}
how can I access "url" and "text"?
String messageLink = remoteMessage.getData().get("link");
gets me:
{"text":"ESPN","url":"http://www.espn.com"}
but how do I drill deeper?
remoteMessage.getData().get("link").get("text");
doesnt quite work... I have also attempted JSONObject:
JSONObject json = new JSONObject(remoteMessage.getData());
JSONObject link = json.getJSONObject("link");
but this gives me try catch errors...
Any help and direction as always is greatly appreciated!
I would use gson and define a model class. The remote message gives you a Map<String, String> and their is no matching constructor for creating a json object.
Add gson to your build.xml:
compile 'com.google.code.gson:gson:2.5'
Create a notification model:
import com.google.gson.annotations.SerializedName;
public class Notification {
#SerializedName("title")
String title;
#SerializedName("message")
String message;
#SerializedName("link")
private Link link;
public String getTitle() {
return title;
}
public String getMessage() {
return message;
}
public Link getLink() {
return link;
}
public class Link {
#SerializedName("url")
String url;
#SerializedName("text")
String text;
public String getUrl() {
return url;
}
public String getText() {
return text;
}
}
}
Deserialize a notification object from the remote message.
If all your custom keys are at the top level:
Notification notification = gson.fromJson(gson.toJson(remoteMessage.getData()), Notification.class);
If your custom json data is nested in a single key for example "data" then use:
Notification notification = gson.fromJson(remoteMessage.getData().get("data"), Notification.class);
Note in this simple case the #SerializedName() annotations are unnecessary since the field names exactly match the keys in the json, but if you for example have a key name start_time but you want to name the java field startTime you would need the annotation.
As simple as that:
String linkData = remoteMessage.getData().get("link");
JSONObject linkObject = new JSONObject(linkData);
String url = linkObject.getString("url");
String text = linkObject.getString("text");
Of course, together with proper error handling.
Faced this issue when migrating from GCM to FCM.
The following is working for my use case, so perhaps it will work for you.
JsonObject jsonObject = new JsonObject(); // com.google.gson.JsonObject
JsonParser jsonParser = new JsonParser(); // com.google.gson.JsonParser
Map<String, String> map = remoteMessage.getData();
String val;
for (String key : map.keySet()) {
val = map.get(key);
try {
jsonObject.add(key, jsonParser.parse(val));
} catch (Exception e) {
jsonObject.addProperty(key, val);
}
}
// Now you can traverse jsonObject, or use to populate a custom object:
// MyObj o = new Gson().fromJson(jsonObject, MyObj.class)

return any exception in json in rest api

Is there any simple methods to return exception in JSON using Rest api?
I've already googled this question, but all solutions i see, was about throwing exceptions during some calculations. But what if income parameters are wrong? I mean what if there is sone string instead of int input parameter?
I created some DTO class for input data:
#XmlRootElement
public class RequestDTO implements Serializable{
private static final long serialVersionUID = 1L;
#XmlElement(name = "request_id")
private String requestId;
#XmlElement(name = "site")
private List<String> sitesIds;
#XmlElement(name = "date_begin")
#JsonSerialize(using = DateSerializer.class)
#JsonDeserialize(using = DateDeserializer.class)
private Date dateBegin;
#XmlElement(name = "date_end")
#JsonSerialize(using = JsonDateSerializer.class)
#JsonDeserialize(using = JsonDateDeserializer.class)
private Date dateEnd;
#XmlElement(name = "volume")
private double volume;
// there is getters and setters
}
If i sent something like 'qwerty' instead of 'volume' field in my json request i'l see error message like Runtime. Is it possible to handle it in someway? I mean to return error in json with such structure?
public class ExceptionDTO {
private String shortExceptionMessage;
private String stackTrace;
public ExceptionDTO(String shotExceptionMessage, String stackTrace){
this.shortExceptionMessage = shotExceptionMessage;
this.stackTrace = stackTrace;
}
public String getShortExceptionMessage() {
return shortExceptionMessage;
}
public String getStackTrace() {
return stackTrace;
}
}
UPD1:
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception>{
#Override
public Response toResponse(final Exception e) {
StringBuilder trace = new StringBuilder();
IntStream.range(0, e.getStackTrace().length)
.forEach(i -> trace.append(e.getStackTrace()[i]).append('\n'));
ExceptionDTO exceptionMessage = new ExceptionDTO(
e.toString(),
trace.toString()
);
return Response.status(500).entity(exceptionMessage).build();
}
}
As it's not really clear if you are interested on checking if field or value of the payload is correct, here are a few ways to work with both.
If you want to check if the value for a field is correct (ie volume field value should be greater than zero etc), check out bean validation. This makes use of annotations on the fields you want to verify.
// for example
#Min(value = 0, message = "invalid message")
private double range;
To use your ExceptionDTO as error response whenever one of those validation fails, you can do so by creating an ExceptionMapper<ConstraintViolationException>. check it here for more details.
If you are checking for the invalid field (ie client sends ragne fields instead of range), have a look at the stack trace on what exception is being thrown. Then register an exception mapper with your ExceptionDTO as body.
For example, if UnrecognizedPropertyException is thrown then you can add:
#Provider
public class UnrecognizedPropertyExceptionMapper implements ExceptionMapper<UnrecognizedPropertyException> {
#Override
public Response toResponse(UnrecognizedPropertyException e) {
ExceptionDTO myDTO = // build response
return Response.status(BAD_REQUEST).entity(myDTO).build();
}
}
If you want to validate input parameters in the request, you should return status code 400 (Bad Request) along with the error details. You can simply send json
{ "error": { "message": "string received for parameter x, where as int expected" } with the response status code 400.
`
I did a bit of research and determined that the best way to encode a Java exception in JSON is to use a convention developed by Oasis that looks like this:
{
"error": {
"code": "400",
"message": "main error message here",
"target": "approx what the error came from",
"details": [
{
"code": "23-098a",
"message": "Disk drive has frozen up again. It needs to be replaced",
"target": "not sure what the target is"
}
],
"innererror": {
"trace": [ ... ],
"context": [ ... ]
}
}
}
details is a list that should have an entry for each nested cause exception in the chain.
innererror.trace should include the stack trace if you wish, as a list of string values.
The response status code should be 400 unless you have a good reason for making it something else, and the code in the structure should match whatever you sent.
Write one method to convert a Java exception to this format, and you are done. Use it consistently and your JS code will be able to handle and display the exception values.
More of the details of the other approaches evaluated and dismissed are covered in this blog post on JSON REST API – Exception Handling
https://agiletribe.purplehillsbooks.com/2015/09/16/json-rest-api-exception-handling/
Here is the java method to convert an exception to this format:
public static JSONObject convertToJSON(Exception e, String context) throws Exception {
JSONObject responseBody = new JSONObject();
JSONObject errorTag = new JSONObject();
responseBody.put("error", errorTag);
errorTag.put("code", 400);
errorTag.put("target", context);
JSONArray detailList = new JSONArray();
errorTag.put("details", detailList);
String lastMessage = "";
Throwable runner = e;
while (runner!=null) {
String className = runner.getClass().getName();
String msg = runner.toString();
runner = runner.getCause();
JSONObject detailObj = new JSONObject();
detailObj.put("message",msg);
int dotPos = className.lastIndexOf(".");
if (dotPos>0) {
className = className.substring(dotPos+1);
}
detailObj.put("code",className);
System.out.println(" ERR: "+msg);
detailList.put(detailObj);
}
JSONObject innerError = new JSONObject();
errorTag.put("innerError", innerError);
JSONArray stackList = new JSONArray();
runner = e;
while (runner != null) {
for (StackTraceElement ste : runner.getStackTrace()) {
String line = ste.getFileName() + ":" + ste.getMethodName() + ":" + ste.getLineNumber();
stackList.put(line);
}
stackList.put("----------------");
runner = runner.getCause();
}
errorTag.put("stack", stackList);
return responseBody;
}

How to parse JSON Response with GSON (Java / Android)

I just need a quick advice, as i am a total beginner with JSON.
I get the following response from a webserver, which i store in a String:
{
"station62":[
{
"departureTime":1982,
"delay":"-1.0",
"line":"6",
"stationName":"randomname",
"direction":2
}
],
"station63":[
{
"departureTime":1234,
"delay":"-1.0",
"line":"87",
"stationName":"anotherrandomname",
"direction":2
}
],
"station64":[
{
"departureTime":4542,
"delay":"-1.0",
"line":"4",
"stationName":"yetanotherrandomname",
"direction":2
}
],
"station65":[
{
"departureTime":1232,
"delay":"-1.0",
"line":"23",
"stationName":"onemorerandomname",
"direction":2
}
]
}
(Sorry, i dont know how the indent works on here.)
The response is longer, but for this example it is shortened. So what i need is to parse the information of each of these "station"-objects.
I dont need the "station62"-String, i only need "departureTime", "delay", "line", "stationName" and "direction" in a java-object.
I have read this, but i couldnt make it work: https://stackoverflow.com/a/16378782
I am a total beginner, so any help would be really appreciated.
Edit: Here is my code:
I made a wrapper class just like in the example link above. I played with the map types a bit, but no luck so far.
public class ServerResponse
{
private Map<String, ArrayList<Station>> stationsInResponse = new HashMap<String, ArrayList<Station>>();
public Map<String, ArrayList<Station>> getStationsInResponse()
{
return stationsInResponse;
}
public void setStationsInResponse(Map<String, ArrayList<Station>> stationsInResponse)
{
this.stationsInResponse = stationsInResponse;
}
}
The problem is, that this map does not get filled by the gson.fromJSON(...)-call i am showing below. The map size is always zero.
Station class looks like this:
public class Station
{
String line;
String stationName;
String departureTime;
String direction;
String delay;
// getters and setters are there aswell
}
And what i am trying to do is
Gson gson = new Gson();
ServerResponse response = gson.fromJson(jsonString, ServerResponse.class);
where "jsonString" contains the JSON response as a string.
I hope that code shows what i need to do, it should be pretty simple but i am just not good enough in JSON.
EDIT 2
Would i need my JSON to be like this?
{"stationsInResponse": {
"station62": [{
"departureTime": 1922,
"delay": "-1.0",
"line": "8",
"stationName": "whateverrandomname",
"direction": 2
}],
"station67": [{
"departureTime": 1573,
"delay": "-1.0",
"line": "8",
"stationName": "rndmname",
"direction": 2
}],
"station157": [{
"departureTime": 1842,
"delay": "-2.0",
"line": "8",
"stationName": "randomname",
"direction": 2
}]
}}
Here is the working code:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
public class GSONTest {
public static void main(String[] args){
String gsonStr = "{\"stationsInResponse\": { \"station62\":[ { \"departureTime\":1982,\"delay\":\"-1.0\",\"line\":\"6\",\"stationName\":\"randomname\",\"direction\":2} ],\"station63\":[ { \"departureTime\":1981,\"delay\":\"-1.1\",\"line\":\"7\",\"stationName\":\"randomname2\",\"direction\":3} ]}}";
Gson gson = new Gson();
Response response = gson.fromJson(gsonStr, Response.class);
System.out.println("Map size:"+response.getStationsInResponse().size());
for (Iterator iterator = response.getStationsInResponse().keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
ArrayList<Station> stationList = (ArrayList<Station>) response.getStationsInResponse().get(key);
for (Iterator iterator2 = stationList.iterator(); iterator2.hasNext();) {
Station station = (Station) iterator2.next();
System.out.println("Delay: "+station.getDelay());
System.out.println("DepartureTime: "+station.getDepartureTime());
System.out.println("Line: "+station.getLine());
System.out.println("StationName: "+station.getStationName());
}
}
}
}
class Response {
private Map<String, List<Station>> stationsInResponse;
//getters and setters
public Map<String, List<Station>> getStationsInResponse() {
return stationsInResponse;
}
public void setStationsInResponse(Map<String, List<Station>> stationsInResponse) {
this.stationsInResponse = stationsInResponse;
}
}
class Station {
private String departureTime;
public String getDepartureTime() {
return departureTime;
}
public void setDepartureTime(String departureTime) {
this.departureTime = departureTime;
}
public String getDelay() {
return delay;
}
public void setDelay(String delay) {
this.delay = delay;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public String getStationName() {
return stationName;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public String getDirection() {
return direction;
}
public void setDirection(String direction) {
this.direction = direction;
}
private String delay;
private String line;
private String stationName;
private String direction;
}
Output in console is like this(as I shortened your json string):
Map size:2
Delay: -1.0
DepartureTime: 1982
Line: 6
StationName: randomname
Delay: -1.1
DepartureTime: 1981
Line: 7
StationName: randomname2
First I'll point out your mistake, then I'll give you the solution.
The structure you're asking for in your deserialization code looks like this:
{
"stationsInResponse": {
"station1": [
{
"name": "Station 1"
}
],
"station2": [
{
"name": "Station 2"
}
]
}
}
Solution
The deserialization code you really need to deserialize the structure you're getting as input, is as follows:
Gson gson = new Gson();
Type rootType = new TypeToken<Map<String, List<Station>>>(){}.getType();
Map<String, List<Station>> stationsMap = gson.fromJson(json, rootType);
This is because a JSON object, whose properties are unknown at compile-time, which your root object is, maps to a Java Map<String, ?>. The ? represent the expected Java type of the JSON object value, which in your case is either List<Station> or Station[], whichever you prefer.
If you wanted to, you could combine all the stations in the map into one List of stations like so:
List<Station> stations = new ArrayList<>(stationsMap.size());
for (List<Station> stationsList : stationsMap.values()) {
for (Station station : stationsList) {
stations.add(station);
}
}

Categories