Weird behavior when calling org.json.JSONObject.getJSONArray - java

Hey,
I noticed this really weird behavior when calling getJSONArray.
This is my code:
JSONObject jsonObject = new JSONObject(data);
if (!jsonObject.getJSONObject("transfer").has("skill"))
return Collections.emptyList();
JSONArray events = jsonObject.getJSONObject("transfer").getJSONArray("skill");
now, whenever transfer has more than 1 skill elements, everything works great, but when there is only 1 skill element, Im getting an exception:
Caused by: org.json.JSONException:
Value {"id":"2","name":"DOGS"} at
skill of type org.json.JSONObject
cannot be converted to JSONArray
at org.json.JSON.typeMismatch(JSON.java:96)
at org.json.JSONObject.getJSONArray(JSONObject.java:548)
....
Is that makes sense? Do I really have to catch the exception and handle this kind of array specific?
Thanks,
Udi

Any time you retrieve a JSON Object surround your code with a try/catch block. This way your program will continue running when you get any unexpected JSON objects.
As for you error it looks like your JSONArray "skill" is not being stored as an array when there is only one item. If you are using a web service to create the JSON object the problem may lie in the web service.

In the JSON you are reading, when there is one element, it probably has something like:
skill: {"id":"2","name":"DOGS"}
rather than:
skill: [{"id":"2","name":"DOGS"}]
Note the difference between a single object and an array that contains a single object. In your code, you are using getJSONArray to ask for an array, resulting in an exception when it's not an array.
You would need to either check that the value of "skill" is an array before using getJSONArray, or assume it is an array and catch the exception to handle the single object case. Alternately, if you can modify the application that is generating the JSON, you could fix it to put the "skill" object inside an array even when there is just one.

Related

Stringify JSON from in GWT ArrayList then return to array

Currently attempting to stringify a Java ArrayList object in GWT using an interop method to call the native JSON.stringify(ArrayListobj). This results in a perfect JSON representation of the underlying array list contents in the form of an array. This even works for seemingly more complicated Java class objects, but in this instance I'm going to use Strings to demonstrate. First on my creation side:
test = new ArrayList<>();
test.add("first");
test.add("second");
String jsonstr = JSON.stringify(test);
Now based on my JsInterop code, I'm returning an Object (or object array as is the case in the following code shown here):
#JsType(isNative=true, namespace=GLOBAL)
public class JSON {
public native static String stringify(Object obj);
public native static Object[] parse(String obj);
}
So far so good, all of this works perfectly, and the results I get are stringified JSON representation of the contents of the ArrayList
"{"array_0":["first","second"]}"
Then run this bit through the parser:
ArrayList<String> returned = new ArrayList<>();
Object[] var = JSON.parse(jsonstr);
And var IS a proper reprsentation (when looking at the web browsers execution paused) of the underlying data from the first ArrayList. The problem is getting the Object[] of the JSON array converted back to the Java ArrayList object.
I have tried using JSNI code to extract the array elements, which actually in the console tray on the web browser works perfectly, but the compliler tries to outsmart me and renames the array element so my JSNI code can't touch it.
If my code is as above, and I write JSNI something like:
public static native ArrayList objToList(ArrayList add,Object inVal) /*-{
var length = inVal.array.length;
for(var i = 0;i < length; i++){
add.array[i] = inVal.array[i];
}
return add;
}-*/;
Then the compiler will rename the array array_0 so my code that says inVal.array no longer ties in with the data I'm trying to access.
From all the testing I've done this is by far the fastest method of getting the same ArrayList object (its guaranteed to be defined the same way in both places) from one place in the client software to another place in client software (no server involved here) through stringification.
But the information about how to manipulate the JavaScript on a low level in GWT is, lacking at best.
And I've tried every variation on GWT-RPC mechanisms, GWT-Jackson, AutoBeans (if only they supported objects with multiple primitive types!) requestbuilder, you name it.
And NO, before you suggest it I'm not interested in doing a full GWT-JSON parse of the string again, I already did that when originally pulling the thousands of records off the server and pushed them into the Java ArrayList. It takes 200+mS to parse the JSON in GWT, while the browsers JSON parse function processes this string in around 3mS.
GWT uses type-markers to keep track of types and be able to do class cast safety. You must never use stringify with Java classes because you will lose those type-markers and also you will be using internal minimized/encoded symbols. So, this is how GWT internally handles all those types:
List<String> list = new ArrayList<>();
list.add("a"); list.add("b");
console.log("list", list);
console.log("array", list.toArray());
console.log("stringify", Global.JSON.stringify(list.toArray()));
console.log("parse", Global.JSON.parse(Global.JSON.stringify(list.toArray())));
The 'list' contains the obfuscated variable array_8_g$, this might change so you never ever should use this kind of encoding. The array result is ok, but you should notice that it contains various properties (typemarker, casteableTypeMap and __clazz), those extra properties are used to make java casting works, but are not enumerable so are not included in the next result stringify. This stringify result can be parsed-back as a String[], but now the result at parse have not included tye type-markers properties. So, if you immediately save the result of parse in a String[] variable, it will work correctly. But if you cast it as Object and try to cast-it-back to String[] it will fail.
In the jsinterop:base dependency there are 2 utilities Js#cast and Js#uncheckedCast that are helpful in these cases. If the array has a type-marker you can use Js#cast (this utility is the same as standard java casting), if not you must use Js#uncheckedCast.
In this example, the first line will succeed, and the second fails with a class cast exception:
console.log("uncheck", Js.<String[]>uncheckedCast(JSON.parse(JSON.stringify(list.toArray())))[0]);
console.log("check", Js.<String[]>cast(JSON.parse(JSON.stringify(list.toArray())))[0]);
You really should try to avoid mixing native JS code with Java. If you need to do that, then you must understand the internals of how GWT handle types, you must understand JSNI but use it as less as possible and finally understand how JsInterop works, and use it to access to native JS code or expose Java code to the JS world.

Similar Method call that returns lists

So i have some working code that allows me to generate a list of value to be formatted later in the program. it returns a generic List and Its leading to an API from google (QPX Express) but i don't think this matters for the question.
List<TripOption> tripOption = dataFromGoogle() //returns List
I was reading over the documentation and it seems that I can use this same API for other values in different Lists. So essentially I would like to use the same call to the API but i can only return one type of list.
dataFromGoogle(tripOption, cityData); //gives me null pointer exception
So my question would be either what would be a good way to reuse my method to grab different aspects of the API, or ELI5 what causes nullpointers because I always get them and I'm not sure I completely understand why.
Null pointer exceptions are thrown when you have something like this:
Object temp = null;
temp.callMethod();
As the name suggests, the NullPointerException occurs when you have a variable that "points" at a null value and try to access something within it as if it were an object.
What's happening with yours probably has something to do with dataFromGoogle returning null, and then dataFromGoogle attempts to treat it as an object.

How do I access the data in Java objects returned by User.getAddresses() and User.getPhones()?

In com.google.api.services.admin.directory.model.User, many methods, such as getAddresses() and getPhones(), return a Java object that I can’t figure out what to do with.
Using toString(), I can see that the data I want is in the object (and looks like a list of HashMaps), but I can’t figure out how to convert it to a form that I can access, such as a list or array of UserAddress or UserPhone?
I’d also like to know how to build such objects so I can update users (although I suppose it’s likely that the answer to the first part of my question may make how to build them obvious)?
I have been able to update others things that don’t involve such objects, so I don’t need help with reading and updating in general, but I’ve struggling with these object for a couple of days now and can’t figure it out. Thanks.
The Google Directoy API uses JSON for communication. The Java client library used to communicate with the API parses and serializes the JSON to and from Java objects for easier usage in code. Have a look here for an idea of how it works: https://code.google.com/p/google-http-java-client/wiki/JSON
The problem with the current version of the library is that it doesn't serialize all the fields in the User class to usable objects. Instead maps, or list of maps, are used for complex fields. For example the phones field should be turned into a list of UserPhone objects, but instead becomes a list of maps. The only field that is being properly serialized is the name field, being a UserName object.
One way to manually turn the maps into the correct object is to use a json parser like Gson and parse the maps. Like this:
Gson gson = new GsonBuilder().create();
ArrayList phones = (ArrayList) user.getPhones();
for (Object phone : phones) {
UserPhone userPhone = gson.fromJson(phone.toString(), UserPhone.class);
System.out.println(userPhone.getType() + "=" + userPhone.getValue());
}
I believe those methods return a collection, in which case you'll probably want to access the items they contain via their iterator. Something like this should work:
ListIterator addresses = user.getAddresses().iterator();
while(addresses.hasNext()) {
Address address = addresses.next();
// do something with the address
}
If you're not familiar with the Iterator object, this article might help: How to use Iterator
Thank you, but User.getAddresses() and User.getPhones() do not have iterator methods, so neither of the answers proposed here will work.
"ListIterator addresses = user.getAddresses().iterator();" gets the compiler error “The method iterator() is undefined for the type Object”.
"for (UserAddress addr : user.getAddresses())" gets the compiler error “Can only iterate over an array or an instance of java.lang.Iterable”.
I finally found a way to get the job done, though. The follow codes actually works, even though it's difficult to believe this is the way I'm "supposed to" do it.
ArrayList<ArrayMap<String,String>> phones = (ArrayList<ArrayMap<String,String>>)user.getPhones();
for (ArrayMap<String,String> phone : phones)
System.out.println(phone.get("type") + "=" + phone.get("value"));
It’d be nice to get the data into an instance of UserPhone, but I can’t figure out how to do that (other than doing a get & UserPhone.setXxxx for every possible keyword).
I was able to update a phone number by passing User.setPhones() an ArrayList<UserPhone> that I built from scratch.

Jackson - Working with arrays of objects, appending and removing

I'm working with the Jackson API in Java for dealing with JSON. I've been working with it a bit here and there, but nothing too in-depth.
Currently, I'm looking for a good way to take an array of JSON objects (either via a stream or String) that was created from a list of POJOs and append or remove a POJO. In the case of appending, duplicate checking isn't really necessary. As a simple example, let's say I have this array built from a list of Java objects with a single variable named "field":
[{"field":"value"},{"field":"value2"}]
And I'd like to append an object of the same type with "field" set to "value3". I could simply deserialize the whole array into a List of Java Objects, add the new object, then serialize it back into JSON, but that feels like overkill. It would be better if I could use Jackson to simply serialize the new object and append it to the end of the JSON array. The same would apply to removing an existing object from the array.
I've found a way, but strangely, it's over twice as slow as the direct deserialize-add-reserialze method with a list of 500 POJOs that have three fields each, and it only gets worse with more objects.
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getJsonFactory().createJsonParser(input);
JsonGenerator gen = mapper.getJsonFactory().createJsonGenerator(output, JsonEncoding.UTF8);
gen.writeStartArray();
parser.nextToken();
while (parser.nextToken() == JsonToken.START_OBJECT) {
//gen.writeTree(parser.readValueAsTree());
//parser.skipChildren();
//EDIT: This is much faster as the only method in the loop:
gen.copyCurrentStructure(parser);
}
gen.writeTree(mapper.valueToTree(/*new Object to add*/);
gen.writeEndArray();
gen.close();
parser.close();
Even if I don't get each object as a tree and instead move them iteratively as fields/values, it's a bit faster, but still considerably slower than the alternative. Is this to be expected or is there a better way to handle it as streaming data rather than the bulk JSON-to-Java-to-JSON method?
EDIT: AHA! Found that the JsonGenerator can directly copy the current structure from a JsonParser withcopyCurrentStructure(JsonParser). Using this in the while loop is faster and now outruns the bruteforce method by a considerable amount.

JSON handling Array and object with same Java code

I have a simple question for android/java implementing JSON. Is there any method possible to handle an array or object with the same code. In my Json response there are many placed where JSON returns a single argument and some times many arguments in an array. I will have to write a long code to handle whether object contains an array or an object otherwise.
Also if somebody help me, how to check whether a element contains JSONArray or JSONObject?
Simply test (instanceof) the type of the object you get back from the JSON parser to see whether it's an array, dictionary, or single object.
If you want to avoid duplication where the array is an array of the instances you'd get back in the simpler case, first test for non-array, and if that's what you have, create an array and stick the single object into it, before passing it to your generalized array-handling logic.

Categories