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>.
Related
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"
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()) {
...
}
Am retrieving information from my SQLite database to display on CardView
My SQLite database structure is SQLite DB
My class is
public class ServiceRequest{
public String reqid;
public String name;
public String branch;
public Date date;
public Date time;
public String services;
//Getter and setter
.............
.............
}
I can convert this to JSON format using
List<ServiceRequest> reqs = getAllReqs();
List<ServiceRequest> jobservList = new ArrayList<>();
for (ServiceRequest access : reqs) {
ServiceRequest ob = new ServiceRequest();
ob.setId(access.getId());
ob.setBranch(access.getBranch());
ob.setName(access.getName());
ob.setDate(access.getDate());
ob.setTime(access.getTime());
ob.setServices(access.getServices());
jobservList.add(ob);
}
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json2 = gson.toJson(jobservList);
return json2;
but my desired JSONObject format is
{
"100": {
"name": "Rahul Suresh",
"branch": "Koramangala",
"phNumber":"123456",
"date": "2016-08-06",
"time": "16:00",
"reqServices": "Loans"
},
"200": {
"name": "Sidh",
"branch": "Jayanagar",
"phNumber":"182694",
"date": "2016-08-12",
"time": "11:00",
"reqServices": "OpenAcc,SafeDeposit"
}
}
so that I will get one whole JSON object with a single call
JSONObject jb = (JSONObject) jsonObject.get(Integer.toString(id));
100,200 are 'reqid' s
It's possible to achieve this using string builder. But is there any other ways to implement this like using an object mapper along with a class or something..?
If you would like to form the JSON you have shown, you could "pull out" the ID into a HashMap key, then set the value to be your object.
I can't remember how Gson handles the conversion of the object values in the map, but this is the general idea
List<ServiceRequest> reqs = getAllReqs();
HashMap<Integer, ServiceRequest> map = new HashMap<Integer, ServiceRequest>();
for (ServiceRequest access : reqs) {
map.put(access.getId(), access);
}
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json2 = gson.toJson(map); // TODO: Not sure if this will work
return json2;
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");
I am new to stackoverflow.
I am creating an Java application which it will get data from a web server. The data is in json format. Example"
[
{
"item_name": "Adame",
"item_type": "Special",
"item": "Chestplate",
"item_min_lvl": "50",
"enchantment": {
"health": "0.3",
"dam": "24%",
"life": "0.1",
"xp": "24%",
"loot": "22%"
},
"def": "73"
},
{
"item_name": "Sticks'",
"item_type": "Unique",
"item": "Stick",
"item_min_lvl": "4",
"enchantment": {
"health": "0.6",
"mana": "1",
"dam": "12%",
"life": "0.3",
"xp": "17%",
"loot": "17%"
},
"min_dam": "39",
"max_dam": "34"
}
]
I know how to deserialize json using Gson. As you can see, it's started with [. I never deserialize this case before. Also, the json data is not the same(e.g. enchantment). I also searched in Google but I can't find any similar case. Can anyone help me with the code?
Try with this code. You will get the answer of your question. It's an List with 2 items.
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new FileReader(new File("resources/json1.txt")));
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
reader.close();
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<MyJSON>>() {
}.getType();
List<MyJSON> list = gson.fromJson(builder.toString(), listType);
// you can try this form as well
// MyJSON[] list = gson.fromJson(builder.toString(), MyJSON[].class);
for (MyJSON json : list) {
System.out.println(json.toString());
}
...
class MyJSON {
String item_name;
String item_type;
String item;
String item_min_lvl;
Enchantment enchantment;
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\nitem_name:").append(item_name);
builder.append("\nitem_type:").append(item_type);
builder.append("\nitem:").append(item);
builder.append("\nitem_min_lvl:").append(item_min_lvl);
builder.append("\n\nEnchantment Details:");
builder.append("\nhealth:").append(enchantment.health);
builder.append("\ndam:").append(enchantment.dam);
builder.append("\nlife:").append(enchantment.life);
builder.append("\nxp:").append(enchantment.xp);
builder.append("\nloot:").append(enchantment.loot);
return builder.toString();
}
}
class Enchantment {
String health;
String dam;
String life;
String xp;
String loot;
}
output:
item_name:Adame
item_type:Special
item:Chestplate
item_min_lvl:50
Enchantment Details:
health:0.3
dam:24%
life:0.1
xp:24%
loot:22%
item_name:Sticks'
item_type:Unique
item:Stick
item_min_lvl:4
Enchantment Details:
health:0.6
dam:12%
life:0.3
xp:17%
loot:17%
EDIT
The structure of each entry is not same hence you can't use POJO for this type of JSON.
Simply use ArrayList<Map<String, Object>> and access the value based on key from the map.
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<Map<String, Object>>>() {
}.getType();
ArrayList<Map<String, Object>> list = gson.fromJson(builder.toString(), listType);
for (Map<String, Object> json : list) {
for (String key : json.keySet()) {
System.out.println(key + ":" + json.get(key));
}
System.out.println("===========");
}
output:
item_name:Adame
item_type:Special
item:Chestplate
item_min_lvl:50
enchantment:{health=0.3, dam=24%, life=0.1, xp=24%, loot=22%}
def:73
===========
item_name:Sticks'
item_type:Unique
item:Stick
item_min_lvl:4
enchantment:{health=0.6, mana=1, dam=12%, life=0.3, xp=17%, loot=17%}
min_dam:39
max_dam:34
===========
This is actually valid in Java and with GSON:
YourObject[] locs = gson.fromJson (someJsonString, YourObject[].class);
It'll parse and return an array of YourObject. Just create Java Classes that represent your JSON objects, and replace the placeholders as necessary.
EDIT:
As Braj said before, you can create a fully formed POJO, including the other, (non-symmetrical) attributes (I'm borrowing the code from from Braj's answer here):
//... snip ...
class MyJSON
{
String item_name;
String item_type;
String item;
String item_min_lvl;
Enchantment enchantment;
// Heres the other attributes
String min_dam;
String max_dam;
}
//... snip ...
GSON will parse it and set the values to null if they aren't provided in the original JSON.
However, from the other question, it seems that the JSON (Java - JSON Parser Error) for enchantment is provided inconsistently, so this will cause issues. I would recommend sending JSON for enchantment as an array for consistency, then you could structure your POJO as:
//... snip ...
class MyJSON
{
String item_name;
String item_type;
String item;
String item_min_lvl;
Enchantment[] enchantment;
// Heres the other attributes
String min_dam;
String max_dam;
}
//... snip ...