JSON handling Array and object with same Java code - java

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.

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.

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.

security of arrays in annotation

I'm writing a soft that process some annotations. One of annotation's parameters is an array. One object finds this array and pass it to another object to process it. And then findbugs starts to scream that I'm passing a private array that may be mutated by malicious code. so the question is: is that true? can annotation parameters be changed in runtime?
This is true: you pass a reference to an array, and arrays are mutable. The callee can modify this array.
Your best course of action is to pass a copy of that array to the callee instead of the original array, for instance by using Arrays.copyOf().
Alternatively, instead of an array, you may want to return a List instead and use the Collections.unmodifiableList() wrapper since this will avoid unnecessary copies.
Arrays returned through reflection should be a fresh copy every time they are retrieved, so there's no problem.
From a mobile code, or in general code quality, perspective you should expect an array returned or passed as an argument to an untrusted method to be malicious modified. Similarly on the receiver side of things, arrays passed as parameters or returned from callbacks may be malicious modified later. So there arrays need to be copied before handing them out and also as they are received (even before any validation).
#fge mention Lists. When sending these out, an unmodifiable collection cannot be modified by the receiver. Receiving collections is a little more tricky. Obviously taking an untrusted List and wrapping it with unmodifiableList wont work. new ArrayList<>(things) is the way to go. Don't attempt to clone a malicious ArrayList because you cannot be sure what clone actually does.
Obviously, if you have an array of mutable objects, both the array and elements will need to be copied.

Java LinkedList -- Retrieving the index of an Object

I guess this question that would have already been asked here. I searched but could not find anything similar. So here goes --
I have a custom data object Method and Method is as follows --
public Class Method {
List<String> inputParameters;
String resultVariableName;
}
Now i have a LinkedList<Method> which acts as a repository of Method objects.
Given a Method object is there a way in which the correct index of the Method object can be concretely determined.
My question arises from the face that LinkedList class has an indexOf routine but this routine returns the first occurrence of the object but then there is no given that 2 copies of Method object can not reside in the LinkedList(right ?)
Would tagging every Method object as I add it to the LinkedList solve my purpose and if so is there an ideal way to do it ?
EDIT :
Explaining my use case a little further.
My code basically reads a Velocity template top-down and creates Method objects. One Method object is created for every velocity routine encountered.
This explains why the same element can be stored at multiple indices in the LinkedList as there is no real restriction on how many number of time a Velocity routine is called or the inputs/results provided to the Velocity routine.
Now, i have a UI component, one JButton per Method object reference in the LinkedList<Method> by using which the user can click and edit the Method object.
Thus i need to know which exact Method object reference to edit in the event that same elements reside twice or more number of times in the LinkedList<Method>
What do you mean by the "correct" index in the first place? If the linked list can contain the same element twice or more (and be careful here - the list will only contain a reference to a Method object, not the object itself) then which index would be "correct" in your view?
Of course you can just iterate over the linked list yourself and return all indexes at which a given Method reference occurs, but it's not clear what you're trying to do with it.
Note that indexes aren't often used with linked lists to start with, as obtaining the element at a given index is an O(n) operation.
Duplicates are allowed in LinkedList's.
LinkedList does not avoid duplicates, it may have more than one copy.
You can put a logic to avoid multiple instances, extend the linkedlist class and override the add function to check if Method object already exists.
OR
If you want to get all instances of the Method object, you can use a ListIterator and collect all instances of it, and return this collection as a result.
"there is not given 2 copies of Method object can not reside in the LinkedList", if this is a scenario, how will you identify which object to retrieve??
In this case, I would suggest you to use a LinkedHashMap, where you can use a Identifier as a key to uniquely identify a Method's object.

Weird behavior when calling org.json.JSONObject.getJSONArray

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.

Categories