GSON: Help me parse some unknown JSON data - java

Hi have JSON data that can come back with different data so I am writing deserialiser to parse it
So basically it come as this
{
"data": {
"errors": {
"user": [
"Invalid email, username or password. Please try again"
]
}
}
}
OR this
{
"errors": {
"promotion": [
"The code was invalid"
]
}
}
My goal is to extract the strings in the array, I do not know what the field will be called. All I do know is that it come as Data or errors or both then inside I have a unknown field name with an array of messages.
So I wrote a deserialiser
#Override
public APIErrorBody deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String message = ""; /** The error message we are going to display */
/** Backend inconsistency: Unsuccessful response from backend can come with node data -> or errors -> we must check what is there */
JsonObject dataOrErrorsAsRoot = json.getAsJsonObject().get("data").getAsJsonObject();
if(dataOrErrorsAsRoot == null){ //If we do not have data node then lets check for errors
dataOrErrorsAsRoot = json.getAsJsonObject().get("errors").getAsJsonObject();
}
for (Map.Entry<String, JsonElement> entry : dataOrErrorsAsRoot.entrySet())
{
String key = entry.getKey();
JsonElement element = entry.getValue();
DebugUtils.Log(key + " " + element.toString());
if(element.isJsonArray()){
JsonArray jsonArray = element.getAsJsonArray();
for(int i = 0; i < jsonArray.size(); i ++){
message += jsonArray.get(i).getAsString() + "\n";
}
}
}
APIErrorBody apiErrorBody = new APIErrorBody();
apiErrorBody.setErrorMessageToDisplay(message);
DebugUtils.Log("the final message is " + apiErrorBody.getErrorMessageToDisplay());
return apiErrorBody;
}
The problem I have is here
for (Map.Entry<String, JsonElement> entry : dataOrErrorsAsRoot.entrySet())
{
String key = entry.getKey();
JsonElement element = entry.getValue();
DebugUtils.Log(key + " " + element.toString());
if(element.isJsonArray()){
JsonArray jsonArray = element.getAsJsonArray();
for(int i = 0; i < jsonArray.size(); i ++){
message += jsonArray.get(i).getAsString() + "\n";
}
}
}
So the problem is, its not a JSON array even though when I debug, it is. The value contained in the JsonElement 'element' is
{"user":["Invalid email, username or password. Please try again"]}
Which is an array
However, my debugger says this which confuses me a lot o_O
That element is a JSON object that contains a LinkedTreeMap
And that map has a value which a JSON array, how do I get it :)
Someone please help, I am dying

Well the debugger got it right, element.isJsonArray() is a hint that "user" is indeed JsonArray.
You simply got a LinkedTreeMap that is mapping between String keys to values. You give it a key and get a value. The value can be anything. In case of "user" key, the value is JsonArray.

You can use this code .lets say String responseStr=new JSONObject(jsondat);
JSONObject response = new JSONObject(responseStr);
if (response.has("data")) {
JSONObject data = response.getJSONObject("data");
JSONObject error = data.getJSONObject("errors");
JSONArray users = error.getJSONArray("user");
} else if (response.has("errors")) {
JSONObject error = response.getJSONObject("errors");
JSONArray promotion = error.getJSONArray("promotion");
}

I deleted my old answer, this is the new one.
Error on my part, see my backend can send data then errors or just data or just errors as the root nodes. I forgot to access data and then errors
This is the working code
#Override
public APIErrorBody deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String message = ""; /** The error message we are going to display */
/** Backend inconsistency: Unsuccessful response from backend can come with node data -> errors or just errors or just data we must check what is there :S */
JsonObject dataOrErrorsAsRoot = json.getAsJsonObject();
if(dataOrErrorsAsRoot.has("data")){ //Check if the first element is data
dataOrErrorsAsRoot = json.getAsJsonObject().get("data").getAsJsonObject();
if(dataOrErrorsAsRoot.has("errors")){ //Check if an errors element exists in the data element
dataOrErrorsAsRoot = dataOrErrorsAsRoot.get("errors").getAsJsonObject();
}
}else{ //it must be errors only, but we should check in case a 503 happens and we try deserialise and empty body
if(dataOrErrorsAsRoot.has("errors")){
dataOrErrorsAsRoot = json.getAsJsonObject().get("errors").getAsJsonObject();
}
}
if(dataOrErrorsAsRoot != null){
for (Map.Entry<String, JsonElement> entry : dataOrErrorsAsRoot.entrySet()) //Lets loop through each field in the json object we got
{
JsonElement element = entry.getValue();
if(element.isJsonArray()){ //if its an array get each string message and combine it
JsonArray jsonArray = element.getAsJsonArray();
for(int i = 0; i < jsonArray.size(); i++){
message += jsonArray.get(i).getAsString() + "\n";
}
}
}
}
//Fail safe, if we got no message or no json just return some generic message
if(TextUtils.isEmpty(message)){
message = MyApplication.getInstance().getResources().getString(R.string.unknown_error);
}
APIErrorBody apiErrorBody = new APIErrorBody();
apiErrorBody.setErrorMessageToDisplay(message);
return apiErrorBody;
}

Related

Check json array has any item

My program is to get a list of Java objects from the "result" string derived from the AsyncTask. In order to do so, I need to use JsonParser to get an JsonArray. In case the "result" string is [], the JsonArray is also []. How can I detect if there is any item in this Json array. I've tried all of suggestions, even detected at the "result" string, nothing is working for me, could you please help? My JsonArray: []
try{
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(orderList).getAsJsonArray();
System.out.println("Inside fromJasonToJava, array: " + array); //This prints out array: []
lst = new ArrayList<Order>();
if(array.get(0)!= null) { //It goes wrong here, I need a working "if" condition
//Value is not null
for (final JsonElement json : array) {
JSONObject jsonObject = new JSONObject(String.valueOf(json));
System.out.println("Inside fromJasonToJava, jsonObject:" + jsonObject);
}
}
}catch(Exception e){
throw new Exception("Convert json to java error [fromJasonToJava()]", e);
}
Inside your if check for size instead, if you are using org.json.simple.JSONArray
if(array.size() != 0)
or if you using org.json.JSONArray
if(array.length() != 0)
You may use if(array.length() > 0)
You no need to use the if condition as java's for-each is smart enough to handle this.
If the array doesn't have any vale or it's empty then it will not get executed at all.
So use this code:
for (final JsonElement json : array) { // it will not get executed at all if array is empty
JSONObject jsonObject = new JSONObject(String.valueOf(json));
System.out.println("Inside fromJasonToJava, jsonObject:" + jsonObject);
Instead of
if(array.get(0)!= null) { //It goes wrong here, I need a working "if" condition
//Value is not null
for (final JsonElement json : array) {
JSONObject jsonObject = new JSONObject(String.valueOf(json));
System.out.println("Inside fromJasonToJava, jsonObject:" + jsonObject);
}

JSONObject parse dictionary objects

JSON values that I get from server:
{
"Status":0,
"Message":"",
"Result":{"0B":"S.C. Blue Air","0Y":"FlyYeti","1X":"Branson Air"}
}
Getting the result as 'response' after connection and I am able to show my JSON string results on the screen.
JSONObject json = new JSONObject(response);
String status = json.getString("Status");
String message = json.getString("Message");
String result = json.getString("Result");
responseView.setText("Status" + status+ "Message" + message" + Result" + result);
I am okay the results of "Status" and "Message" but not with "Result" because want to separate "Result" objects as and able use each of them as objects.
For example:
When I type OB in my app, I will get the result S.C. Blue Air
Instead of :
String result = json.getString("Result");
use
if(json.get("Result") instanceof JSONObject){
JSONObject object = (JSONObject) json.get("Result");
//do what you want with JSONObject
String ob = object.get("0B");
}
If you want to store it some way you can put it to Map or create object if always it is same data
You can use some libraries such as Gson (Google) or Moshi (Square)
Those libraries allows you to declare your model as a plain java class (commonly called POJOS) annotated in some way that this libraries bind your properties in the JSON to your java properties.
In your case:
JSON:
{
"Status":0,
"Message":"",
"Result":{"0B":"S.C. Blue Air","0Y":"FlyYeti","1X":"Branson Air"}
}
MODEL:
public class MyCallResponse {
#SerializedName("Status")
int status;
#SerializedName("Message")
String message;
#SerializedName("Result")
Result result;
}
public class Result {
#SerializedName("0B")
String b;
#SerializedName("0Y")
String y;
#SerializedName("0X")
String x;
}
In this case, with Gson you can do:
MyCallResponse response = new Gson().fromJson(json, MyCallResponse.class);
Log.i("Response b", response.result.b);
Look at the documentation for more information about both libraries.
try this :
JSONObject json = new JSONObject(response);
JSONObject resultObj = json.getJSONObject("Result");
String OB = resultObj.getString("OB");
Try this
String base = ""; //Your json string;
JSONObject json = new JSONObject(base);
JSONOBject resultJson = json.getJSONObject("Result");
// Get all json keys "OB", "OY", "1X" etc in Result, so that we can get values against each key.
Set<Map.Entry<String, JsonElement>> entrySet = resultJson.entrySet();
Iterator iterator = entrySet.iterator();
for (int j = 0; j < entrySet.size(); j++) {
String key = null; //key = "OB", "OY", "1X" etc
try {
Map.Entry entry = (Map.Entry) iterator.next ();
key = entry.getKey ().toString ();
//key = "OB", "OY", "1X" etc
}
catch (NoSuchElementException e) {
e.printStackTrace ();
}
if (!TextUtils.isEmpty (key)) {
Log.d ("JSON_KEY", key);
String value = resultJson.getString(key);
//for key = "0B", value = "S.C. Blue Air"
//for key = "0Y", value = "FlyYeti"
//for key = "1X", value = "Branson Air"
}
}
It works with any array with dynamic json key.
Don't forget to accept the answer & upvote if it works.

How to store a JsonArray in a Map?

I have used the following Code
JSONArray jsonArray = new JSONArray(incomingData);
Map<String,String> map=new HashMap<String,String>();
for(int i=0;i<jsonArray.length();i++){
JSONObject j = jsonArray.getJSONObject(i);
Iterator<?> it = j.keys();
while (it.hasNext()) {
String n = it.next().toString();
map.put(n, j.getString(n));
}
}
But when I execute this code it's throwing an Exception.
org.json.JSONException: JSONObject["low_stock_date"] not a string.
JsonArray Used
[
{
"item_id": "1",
"product_id": "1",
"stock_id": "1",
"qty": "99.0000",
"low_stock_date": null
},
{
"item_id": "2",
"product_id": "2",
"stock_id": "1",
"qty": "100.0000",
"low_stock_date": null
}
]
Please help me to over come this issue.
The value of low_stock_date is null. thats why it is throwing this error.
Just add one check for null before adding it into Map.
if(!j.isNull(n)){
map.put(n, j.getString(n));
}
else{
map.put(n, null);
}
for(int i=0;i<jsonArray.length();i++){
JSONObject j = jsonArray.getJSONObject(i);
Iterator<?> it = j.keys();
while (it.hasNext()) {
String n = it.next().toString();
map.put(n, j.getString(n));
}
}
You are doing the above, inside for loop & storing the key/value pair in Map.
As same keys are there in every JSONObject so only values in last jsonobject would be there in for Map keys.
As an alternate you can create an ArrayList for storing these Maps, which you can get while converting each JSONObject to Map
One more thing you can do here is using a parser like Jackson Parser
HashMap<String,String> result =
new ObjectMapper().readValue(JSON_SOURCE, HashMap.class);
(where JSON_SOURCE is a File, input stream, reader, or json content String).
That would be quick & better way of doing the what you want.
This is actually how the method is implemented:
/**
* Get the string associated with a key.
*
* #param key
* A key string.
* #return A string which is the value.
* #throws JSONException
* if there is no string value for the key.
*/
public String getString(String key) throws JSONException {
Object object = this.get(key);
if (object instanceof String) {
return (String) object;
}
throw new JSONException("JSONObject[" + quote(key) + "] not a string.");
}
You could check the type of the to-be-loaded object to see if it is null:
j.isNull(n);
and then act accordingly:
if (j.isNull(n)) {
// do something
} else {
// do something else
}

How to get specific JSON data?

Hi I am trying to read JSON from an ReST API but Im getting a nullpointer exception because mycode is not correct.
I have a JSON that I am reading from looking like this :
processJSON({
"LocationList":{
"noNamespaceSchemaLocation":"http://api.vasttrafik.se/v1/hafasRestLocation.xsd",
"servertime":"16:13",
"serverdate":"2013-03-22",
"StopLocation":[{
"name":"Brunnsparken, Göteborg",
"lon":"11.967824",
"lat":"57.706944",
"id":"9021014001760000",
"idx":"1"
},{
"name":"Brunnsgatan, Göteborg",
"lon":"11.959455",
"lat":"57.693766",
"id":"9021014001745000",
"idx":"4"
},{
"name":"Brunnslyckan, Lerum",
"lon":"12.410219",
"lat":"57.812073",
"id":"9021014017260000",
"idx":"5"
},
Now I want the name from the JSON document depending on what the user inputs.
how do I do this with code?
My code that is wrong is like this :
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
public class JSONReader {
private String jsonData = "";
public String getJsonData(String location){
try {
URL url = new URL("http://api.vasttrafik.se/bin/rest.exe/v1/location.name?authKey=secret&format=json&jsonpCallback=processJSON&input=" + URLEncoder.encode(location, "UTF-8"));
URLConnection connection = url.openConnection();
BufferedReader readJsonFile = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String temp = "";
while((temp = readJsonFile.readLine()) != null){
jsonData += temp;
}
readJsonFile.close();
System.out.println(jsonData);
return jsonData;
}
catch (IOException e) {
}
return null;
}
public void JSONParsing(){
String location = Planner.getPlanner().getStartingLocation();
JSONObject obj =(JSONObject)JSONValue.parse(getJsonData(location));
//Set the text into the JList
if (obj.containsValue(location));
obj.get("name");
}
}
I want get the same name of the location out from the JSON as the user inputs.
How do I do this with code?
I think that you are asking how to parse your JSONObject and get the corresponding values out of it that the user is interested in. Below is an example of how you can pull apart the JSONObject to create a Map whose key is the String id (since the name does not seem to be unique) and whose value is the whole JSONObject. You can use this map to lookup the input from the user and find the appropriate LLA if that's what you are interested in.
public Map<String, JSONObject> createLocationMap(JSONObject jsonObj){
Map<String, JSONObject> nameToLocationMap = new HashMap<String, JSONObject>();
JSONObject locationList = (JSONObject) jsonObj.get("LocationList");
JSONArray array = (JSONArray) locationList.get("StopLocation");
for (int i = 0; i < array.length(); i++) {
String name = (String) ((JSONObject) array.get(i)).get("id");
nameToLocationMap.put(name, ((JSONObject)array.get(i)));
}
return nameToLocationMap;
}
You can tailor this method as you see fit. For example if you are interested in the relationship between the id and the name then you can create a similar method that uses those values instead of id and the entire JSONObject'. I hope that this helps~

Test if it is JSONObject or JSONArray

I have a json stream which can be something like :
{"intervention":
{
"id":"3",
"subject":"dddd",
"details":"dddd",
"beginDate":"2012-03-08T00:00:00+01:00",
"endDate":"2012-03-18T00:00:00+01:00",
"campus":
{
"id":"2",
"name":"paris"
}
}
}
or something like
{"intervention":
[{
"id":"1",
"subject":"android",
"details":"test",
"beginDate":"2012-03-26T00:00:00+02:00",
"endDate":"2012-04-09T00:00:00+02:00",
"campus":{
"id":"1",
"name":"lille"
}
},
{
"id":"2",
"subject":"lozlzozlo",
"details":"xxx",
"beginDate":"2012-03-14T00:00:00+01:00",
"endDate":"2012-03-18T00:00:00+01:00",
"campus":{
"id":"1",
"name":"lille"
}
}]
}
In my Java code I do the following:
JSONObject json = RestManager.getJSONfromURL(myuri); // retrieve the entire json stream
JSONArray interventionJsonArray = json.getJSONArray("intervention");
In the first case, the above doesn't work because there is only one element in the stream..
How do I check if the stream is an object or an array ?
I tried with json.length() but it didn't work..
Thanks
Something like this should do it:
JSONObject json;
Object intervention;
JSONArray interventionJsonArray;
JSONObject interventionObject;
json = RestManager.getJSONfromURL(myuri); // retrieve the entire json stream
Object intervention = json.get("intervention");
if (intervention instanceof JSONArray) {
// It's an array
interventionJsonArray = (JSONArray)intervention;
}
else if (intervention instanceof JSONObject) {
// It's an object
interventionObject = (JSONObject)intervention;
}
else {
// It's something else, like a string or number
}
This has the advantage of getting the property value from the main JSONObject just once. Since getting the property value involves walking a hash tree or similar, that's useful for performance (for what it's worth).
Maybe a check like this?
JSONObject intervention = json.optJSONObject("intervention");
This returns a JSONObject or null if the intervention object is not a JSON object. Next, do this:
JSONArray interventions;
if(intervention == null)
interventions=jsonObject.optJSONArray("intervention");
This will return you an array if it's a valid JSONArray or else it will give null.
To make it simple, you can just check first string from server result.
String result = EntityUtils.toString(httpResponse.getEntity()); //this function produce JSON
String firstChar = String.valueOf(result.charAt(0));
if (firstChar.equalsIgnoreCase("[")) {
//json array
}else{
//json object
}
This trick is just based on String of JSON format {foo : "bar"} (object)
or [ {foo : "bar"}, {foo: "bar2"} ] (array)
You can get the Object of the input string by using below code.
String data = "{ ... }";
Object json = new JSONTokener(data).nextValue();
if (json instanceof JSONObject)
//do something for JSONObject
else if (json instanceof JSONArray)
//do something for JSONArray
Link: https://developer.android.com/reference/org/json/JSONTokener#nextValue
Object valueObj = uiJSON.get(keyValue);
if (valueObj instanceof JSONObject) {
this.parseJSON((JSONObject) valueObj);
} else if (valueObj instanceof JSONArray) {
this.parseJSONArray((JSONArray) valueObj);
} else if(keyValue.equalsIgnoreCase("type")) {
this.addFlagKey((String) valueObj);
}
// ITERATE JSONARRAY
private void parseJSONArray(JSONArray jsonArray) throws JSONException {
for (Iterator iterator = jsonArray.iterator(); iterator.hasNext();) {
JSONObject object = (JSONObject) iterator.next();
this.parseJSON(object);
}
}
I haven't tryied it, but maybe...
JsonObject jRoot = RestManager.getJSONfromURL(myuri); // retrieve the entire json stream
JsonElement interventionElement = jRoot.get("intervention");
JsonArray interventionList = new JsonArray();
if(interventionElement.isJsonArray()) interventionList.addAll(interventionElement.getAsJsonArray());
else interventionList.add(interventionElement);
If it's a JsonArray object, just use getAsJsonArray() to cast it. If not, it's a single element so just add it.
Anyway, your first exemple is broken, you should ask server's owner to fix it. A JSON data structure must be consistent. It's not just because sometime intervention comes with only 1 element that it doesn't need to be an array. If it has only 1 element, it will be an array of only 1 element, but still must be an array, so that clients can parse it using always the same schema.
//returns boolean as true if it is JSONObject else returns boolean false
public static boolean returnBooleanBasedOnJsonObject(Object jsonVal){
boolean h = false;
try {
JSONObject j1=(JSONObject)jsonVal;
h=true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if(e.toString().contains("org.json.simple.JSONArray cannot be cast to org.json.simple.JSONObject")){
h=false;
}
}
return h;
}

Categories