I have this JSON response, from a remote server and i really hope i can get help.
{
"data": {
"6111": {
"prereq": "0",
"mast": "The Master Tree"
},
"6112": {
"prereq": "1",
"mast": "Another Master Tree"
}
}
}
I use GSON to parse JSON, using the #SerializedName and #Exposeto obtain the value into a custom Model. But i do not understand how to get past the
"6111"
"6112"
I have checked other questions via the gson tag, to no avail.
try this
Iterator<String> iter = json.keys();
while (iter.hasNext()) {
String key = iter.next();
try {
Object value = json.get(key);
} catch (JSONException e) {
// Something went wrong!
}
}
///////////////////update////////////////////
JSONObject issueObj = new JSONObject(jsonContent);
Iterator iterator = issueObj.keys();
while(iterator.hasNext()){
String key = (String)iterator.next();
JSONObject issue = issueObj.getJSONObject(key);
// get id from issue
String _pubKey = issue.optString("id");
}
If you're using Gson, any time you have an object with keys you don't know ahead of time, you can just use Map instead of a custom object.
In this case, each element of the Map will be some "known" data structure, so you would use Map<String, MyObject>.
Your top-level class:
public class MyResponse {
#SerializedName("data")
#Expose
private Map<String, MyObject> data;
...
}
And your map's value class:
public class MyObject {
#SerializedName("prereq")
#Expose
private String prereq;
#SerializedName("mast")
#Expose
private String mast;
...
}
In the specific case of the json text you posted, you would then be able to use these objects like this:
response.getData().get("6111").getMast();
But you can also do anything you could normally do with a Map:
Map<String, MyObject> data = response.getData();
for (String key: data.keySet() {
...
}
for (MyObject obj : data.values()) {
...
}
Related
I am getting nested json object and json array as response while making ReST call. Nested json array and json object comes randomly. It may not be present as part of response everytime. I want to deserialize json in such a way that all the fields in json place at the root level of java object.
JSON Response
{
"data": {
"id":"42342sdz",
"details" : {
"username": "Username",
"location": "Location",
"history":[
{
"historyId":"34312cs", "historyDetail":"Some val", "datetime":"Some val",
"myObj":{
"myField" : "Some val"
}
},
{ "historyId":"34312cs", "historyDetail":"Some val", "datetime":"Some val"}
]
}
}
}
Java Object which I want to build by parsing above JSON response.
class ResponseObject {
String id;
String username;
String location;
List<History> historyList;
//Getters and Setters
}
class History {
String historyId;
String historyDetails
String datetime;
String myField;
//Getters and Setters
}
I'm not really sure what you mean when you say that the JSON object comes randomly. If you mean that the fields themselves are random (with random labels), then I'm not confident that you can store them as fields in a Java object like that. However, if you know the fields beforehand, then you can tell Jackson (the JSON deserializer that Spring Boot uses) how to deserialize the object by adding a method into your ResponseObject class that looks like this:
#SuppressWarnings("unchecked")
#JsonProperty("data")
private void unpackNested(Map<String, Object> data) {
this.id = (String) data.get("id");
Map<String, Object> details = (Map<String, Object>) data.get("details");
this.username = (String) details.get("username");
this.location = (String) details.get("location");
List<Map<String, Object>> originalHistory = (List<Map<String, Object>>) details.get("history");
historyList = new ArrayList<>();
if (originalHistory != null) {
for (Map<String, Object> h : originalHistory) {
History history = new History();
history.setHistoryId((String) h.get("historyId"));
history.setHistoryDetails((String) h.get("historyDetail"));
history.setDatetime((String) h.get("datetime"));
Map<String, Object> myObj = (Map<String, Object>) h.get("myObj");
if (myObj != null) {
history.setMyField((String) myObj.get("myField"));
}
historyList.add(history);
}
}
}
If you don't know the names of the fields, then I think the best you can do is store it into a flat Map<String, Object>.
I have a Cosmos DB and want to write different kind of documents to it. The structure of the documents is dynamic and can change.
I tried the following. Let's say I have the following class:
class CosmosDbItem implements Serializable {
private final String _id;
private final String _payload;
public CosmosDbItem(String id, String payload) {
_id = id;
_payload = payload;
}
public String getId() {
return _id;
}
public String getPayload() {
return _payload;
}
}
I can create then the document with some JSON as follows:
CosmosContainer _container = ...
CosmosDbItem dataToWrite = new CosmosDbItem("what-ever-id-18357", "{\"name\":\"Jane Doe\", \"age\":42}")
item = _cosmosContainer.createItem(dataToWrite, partitionKey, cosmosItemRequestOptions);
This results in a document like that:
{
"id": "what-ever-id-18357",
"payload": "{\"name\":\"Jane Doe\", \"age\":42}",
"_rid": "aaaaaaDaaAAAAAAAAAA==",
"_self": "dbs/aaaaAA==/colls/aaaaAaaaDI=/docs/aaaaapaaaaaAAAAAAAAAA==/",
"_etag": "\"6e00c443-0000-0700-0000-5f8499a70000\"",
"_attachments": "attachments/",
"_ts": 1602525607
}
Is there a way in generating the payload as real JSON object in that document? What do I need to change in my CosmosDbItem class? Like this:
{
"id": "what-ever-id-18357",
"payload": {
"name":"Jane Doe",
"age":42
},
"_rid": "aaaaaaDaaAAAAAAAAAA==",
"_self": "dbs/aaaaAA==/colls/aaaaAaaaDI=/docs/aaaaapaaaaaAAAAAAAAAA==/",
"_etag": "\"6e00c443-0000-0700-0000-5f8499a70000\"",
"_attachments": "attachments/",
"_ts": 1602525607
}
Here is my solution that I ended up. Actually it is pretty simple once I got behind it. Instead of using CosmosDbItem I use a simple HashMap<String, Object>.
public void writeData() {
...
Map<String, Object> stringObjectMap = buildDocumentMap("the-id-", "{\"key\":\"vale\"}");
_cosmosContainer.createItem(stringObjectMap, partitionKey, cosmosItemRequestOptions);
...
}
public Map<String, Object> buildDocumentMap(String id, String jsonToUse) {
JSONObject jsonObject = new JSONObject(jsonToUse);
jsonObject.put("id", id);
return jsonObject.toMap();
}
This can produce the following document:
{
"key": "value",
"id": "the-id-",
"_rid": "eaaaaaaaaaaaAAAAAAAAAA==",
"_self": "dbs/eaaaAA==/colls/eaaaaaaaaaM=/docs/eaaaaaaaaaaaaaAAAAAAAA==/",
"_etag": "\"3b0063ea-0000-0700-0000-5f804b3d0000\"",
"_attachments": "attachments/",
"_ts": 1602243389
}
One remark: it is important to set the id key in the HashMap. Otherwise one will get the error
"The input content is invalid because the required properties - 'id; ' - are missing"
ArrayList<StylistArray>stylistsArr=new ArrayList<StylistArray>();
stylistsArr.add(new StylistArray("BqYWKWzs4r8SyGQvyqH2","18:30"));
stylistsArr.add(new StylistArray("at5kjx5FqIxbnMys8w4q","18:30"));
stylistsArr.add(new StylistArray("nFI5hxfIePx240dmqR0R","18:00"));
stylistsArr.add(new StylistArray("spxSj8UZem5uD0wL46EP","18:20"));
.
https://us-central.jshshskajshdala.net?dasa="+"2018-09-04"+"&ewsss="+"17:50"+"&stylist="+String.valueOf(stylistsArr)
I want to pass this arrayList along with the url. When I try to pass the array values the name of the model class is passing instead of values in the class
Example
I want to pass values like this:
[
{
"stylist": "BqYWKWzs4r8SyGQvyqH2",
"endTime": "18:30"
},
{
"stylist": "at5kjx5FqIxbnMys8w4q",
"endTime": "18:30"
},
{
"stylist": "nFI5hxfIePx240dmqR0R",
"endTime": "18:00"
},
{
"stylist": "spxSj8UZem5uD0wL46EP",
"endTime": "18:00"
}
]
Like #Mathan and #Cris say, you can simply using for looping to create your JSON string. For the following simple pojo:
public class StylistArray {
// use a final so the value can't be change once the object is created.
private final String stylist;
private final String endTime;
public StylistArray(String stylist, String endTime) {
this.stylist = stylist;
this.endTime = endTime;
}
public String getStylist() {
return stylist;
}
public String getEndTime() {
return endTime;
}
}
You can simply doing the following:
List<StylistArray> list = new ArrayList<>();
// assume you have add all items to the list.
JSONArray jsonArray = new JSONArray();
try {
// You need to use simple for loop instead the following foreach
// because foreach is slower than traditional loop.
for (StylistArray stylistArray : list) {
// create JSON object for each item
JSONObject jsonObject = new JSONObject();
jsonObject.put("stylist", stylistArray.getStylist());
jsonObject.put("endTime", stylistArray.getEndTime());
// append it to your JSON array.
jsonArray.put(jsonObject);
}
} catch (JSONException e) {
e.printStackTrace();
// Error happens, try to handle it.
}
Then you can get the string from the JSONArray. I'll let you to find out ;)
Add the Gson dependency in gradle
implementation 'com.google.code.gson:gson:2.8.4'
and rebuild your project
String json= new Gson().toJson(stylistsArr);
Now your "json" variable will have a json array.
Here is the code
List<Map<String, String>> values = new ArrayList<Map<String, String>>();
Map<String, String> maps = new HashMap<>();
maps.put("stylist", "BqYWKWzs4r8SyGQvyqH2");
maps.put("endTime", "18:30");
values.add(maps);
maps.put("stylist", "at5kjx5FqIxbnMys8w4q");
maps.put("endTime", "18:30");
values.add(maps);
maps.put("stylist", "nFI5hxfIePx240dmqR0R");
maps.put("endTime", "18:00");
values.add(maps);
JSONArray mJSONArray = new JSONArray(values);
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)
I have a Json string which has a string message field.
String:
{ "Type" : "Text",
"Subject" : "data received",
"Message" :"{\\"language\\":\\"US\\",\\"data\\":\\"signature\\"}"
}
I want to convert it into the following structure:
Notification.java
public class Notification {
String type;
String subject;
Message message;
}
Message.java
public class Message {
String language;
String data;
}
Is there a way in which I can directly convert the string to a Java object of the above structure? I want to avoid deserializing twice.
You can create a custom Deserializer to deserialize the Message text into Message object and annotate the Message class with #JsonDeserialize:
#JsonDeserialize(using = MessageDeserializer.class)
public class Message {
String language;
String data;
}
public class MessageDeserializer extends JsonDeserializer<Message> {
public MessageDeserializer() {
super();
}
#Override
public Message deserialize(
final JsonParser jsonParser, final DeserializationContext deserializationContext) throws
IOException, JsonProcessingException {
final String messageText = jsonParser.getText();
// parse messageText into Message object
}
}
I am not sure my solution is acceptable since it does require additional explicit call to ObjectMapper to perform deserialization of the string value of Message.
However, this is it is done during the buildup of Notification object and does not require a String message property.
You need to add a ctor with String argument to Message class, where you can deserialize the String into Map and extract the instance propertieds:
public Message(String str) {
try {
#SuppressWarnings("unchecked")
Map<String, Object> map =
(Map<String, Object>)new ObjectMapper().readValue(str, Map.class);
language = map.containsKey("language") ? map.get("language").toString() : null ;
data = map.containsKey("data") ? map.get("data").toString() : null ;
} catch (Exception e) {
e.printStackTrace();
}
}
the new ctor will be called by Jackson when you deserialize a Notification object:
Notification n = (Notification)new ObjectMapper().readValue(reader, Notification.class);
You can convert json string into key-value pairs in Map.You will have to do twice as the Message value is again a json string.Use org.json for JSONObject
Map<String, String> map = new HashMap<String, String>();
JSONObject j = new JSONObject(str);
Iterator<String> keys = j.keys();
while( keys.hasNext() ){
String key = (String)keys.next();
String val = j.getString(key);
map.put(key, val);}
Then retrieve the values by iterating over the keys and pass the values into the class constructor
Then map.get(key) can be used to retrieve the values and will be passed into constructors of the classes.
The org.json library is easy to use:
//Create Json object to parse string
// str is input string
JSONObject obj = new JSONObject(str);
//Create Message
Message mess = new Message();
JSONObject obj2 = new JSONObject(obj.getString("Message"));
mess.data = obj2.getString("data");
mess.language = obj2.getString("language");
//Create Notification
Notification noti = new Notification();
noti.message = mess;
noti.subject = obj.getString("Subject");
noti.type = obj.getString("Type");