Flattening a JSONObject in Java - Recursion causing StackOverflowError - java

I've been writing a method to "flatten" a codehaus JSONObject in Java. Unfortunately, I'm seeing a StackOverflowError in the recursion through the object nests, but I'm finding it difficult to debug. Here is the error I'm seeing:
Exception in thread "main" java.lang.StackOverflowError
at java.util.LinkedHashMap$LinkedHashIterator.<init>(LinkedHashMap.java:345)
at java.util.LinkedHashMap$LinkedHashIterator.<init>(LinkedHashMap.java:345)
at java.util.LinkedHashMap$KeyIterator.<init>(LinkedHashMap.java:383)
at java.util.LinkedHashMap$KeyIterator.<init>(LinkedHashMap.java:383)
at java.util.LinkedHashMap.newKeyIterator(LinkedHashMap.java:396)
at java.util.HashMap$KeySet.iterator(HashMap.java:874)
at org.codehaus.jettison.json.JSONObject.keys(JSONObject.java:533)
at org.codehaus.jettison.json.JSONObject.toString(JSONObject.java:1079)
at org.codehaus.jettison.json.JSONObject.valueToString(JSONObject.java:1210)
I'm using Iterator to loop the keys, and using hasNext() and next() to ensure that I should only be able to access specific object keys.
I started testing with a simple JSONObject of:
JSONObject json = new JSONObject("outer":{"field1":"value","inner":{"field2":12345,"field3":"example#example.com"}});
/*
"outer":{
"field1":"value",
"inner":{
"field2":12345,
"field3":"example#example.com"
}
}
*/
This should result in a single nest containing fields1|2|3.
Here is the code I have so far:
private static JSONObject flatten(JSONObject object, JSONObject flattened){
if(flattened == null){
flattened = new JSONObject();
}
Iterator<?> keys = object.keys();
while(keys.hasNext()){
String key = (String)keys.next();
try {
if(object.get(key) instanceof JSONObject){
flattened.put(key, flatten(object.getJSONObject(key), flattened));
} else {
flattened.put(key, object.get(key));
}
} catch(JSONException e){
System.out.println(e);
}
}
return flattened;
}
I have been debugging this for a while now, but haven't been able to make any headway - so I'd appreciate any pointers with this. Thanks in advance for any help - if any more info is needed, just leave a comment.

Replace
flattened.put(key, flatten(object.getJSONObject(key), flattened));
by
flatten(object.getJSONObject(key), flattened);
Here it gives me {"field1":"value","field2":12345,"field3":"example#example.com"} and I think that's what you want

Notice that when you call the function recursively, you pass the "flattened" object into the function, and then it returns it back to you, which you then add to "flattened". Thus you are adding the object to itself, creating a circular reference
When you do the recursive call, don't add the result back into the object. Just do:
flatten(object.getJSONObject(key), flattened);

Related

Fetch value from a set object in java

I'm iterating a set object to find a particular value. Is there any short way to fetch instead of iterating it? Here is my code
for(Tree t : assignedTrees) {
println t.treeName;
}
The above code will return the expected value.
assignedTrees is the set object
Set<Tree> assignedTrees = new HashSet<Tree>()
println assignedTrees will return
[Tree{id=null, treeName=Mango}]
Can I fetch the treeName instead of iterating?
You can fetch an object from a set by calling mySet.get(object). However, in your case you wish to fetch an object based on one of its attributes. The best way to do this is with a map - e.g.
Map<String, Tree> trees = new HashMap<>();
trees.put(treeObject.treeName, treeObject);
Tree myTree = trees.get("myTreeName");
Note that if you're putting your own objects into sets or maps, you must override the equals and hashcode methods, or very strange things will happen.
In general you can use lambda to find any/first element that fullfils any condition. For example:
Set<Integer> coolStrings = new HashSet<String>();
coolStrings.add("HEHE")
coolStrings.add("NOPE")
coolStrings.add("JP2GMD")
coolStrings.add("1234")
try{
String nice =
coolStrings.stream().filter(
(str) -> { return str.equals("JP2GMD") ||
str.equals("2137"); }
}).findFirst().get();
)
System.out.println("Yay, i found a REALLY cool string! : " + nice);
}
catch(NoSuchElementException e){
System.out.println("Not even one awesome string was found :(");
}
It will print "JP2GMD"
(I didn't compile it, there might be some minor syntax errors)
Working with Stream class is extremally handy (as for java standards)

Print ParseObject content

perhaps this is a silly question but, I'm trying just to print ALL the content (not just one field) of a ParseObject (Parse platform). But I didn't found anything about that, either the API, forums or here.
toString method it's not overridden inside the API, and there is no "toJsonString" or "toJson" or "getValues" nothing like that.
Any suggestion?
Cheers.
Unfortunately, there is no methods provided by `Parse' to do that. But you can use keySet() method to implement that. It's quick sample to give your direction.
try {
TestParse parseObject = ParseObject.createWithoutData(TestParse.class, "tjkbdde0B1");
JSONObject jsonObject = parseObjectToJson(parseObject);
Log.d("TAG", jsonObject.toString(4));
} catch (ParseException | JSONException e) {
Log.e("TAG", e.getMessage());
}
Method to convert to JSON object
private JSONObject parseObjectToJson(ParseObject parseObject) throws ParseException, JSONException {
JSONObject jsonObject = new JSONObject();
parseObject.fetchIfNeeded();
Set<String> keys = parseObject.keySet();
for (String key : keys) {
Object objectValue = parseObject.get(key);
if (objectValue instanceof ParseObject) {
jsonObject.put(key, parseObjectToJson(parseObject.getParseObject(key)));
// keep in mind about "pointer" to it self, will gain stackoverlow
} else if (objectValue instanceof ParseRelation) {
// handle relation
} else {
jsonObject.put(key, objectValue.toString());
}
}
return jsonObject;
}
You can extend it to care about other possible types at Parse, all in your hands.
Is this what you're looking for?
myParseObject.getJSONObject(myKey).toString();
ParseObject getJSONObject method
JSONObject toString method
From ParseObject, you'll have to get each field individually. Depending on the datatype, use something like myParseObject.getString("myStringFieldName") or myParseObject.getInt("myIntFieldName"). Then, once you have that value, you can do whatever you like with it.
https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#toJSON
Example:
console.log( feature.toJSON() );

Dealing with Exceptions and Errors where errors are OK

I have a JSON string into which I'm adding multiple variables. The problem here is that sometimes not all the data will have something entered, and therefore return a NullPointerException.
For example, here's some code:
public static void main(String[] args) throws JSONException {
JSONObject obj = new JSONObject();
obj.put("att1", val1);
obj.put("att2", val2);
obj.put("att3", val3);
obj.put("att4", val4);
obj.put("att5", val5);
obj.put("att6", val6);
System.out.println(obj.toString());
}
This will work, because I'm throwing an exception. However, say one time I run it, and val4 doesn't exist, I'll get a JSONException and my whole code will stop. What I really want is for the code to say "OK, nothing by the name of val4, so let's ignore it and move to the next line of code.
The methods I know of are using successive if statements (for each attribute I check if it's null before trying to add it) or using multiple try/catches.
I know ignoring errors is a massive taboo, so instead of saying I was wondering if there was a method of ignoring the error and just continuing with the next line of code, is there a good way to check each individual line before entering it and if there is a null value, ignore the line before carrying on?
(although, I would be happy to ignore errors on this one block of code)
Cheers and TIA,
Kesh
It looks like you only want to add the pair if the value is not null. JSONObject#putOpt does just this.
Reference:
https://developer.android.com/reference/org/json/JSONObject.html#putOpt(java.lang.String,%20java.lang.Object)
If you're interested in ignoring the exception and continue populating the JSONObject you can have your values ready in a Map and use a loop like this (while ignoring any JSONExceptions that may get thrown)
JSONObject jsonObj = new JSONObject();
for (Map.Entry<String, String> entry : valMap.entrySet()) {
try {
jsonObj.put(entry.getKey(), entry.getValue()); // attr4, null
} catch (JSONException ignored) {}
}
Which data structure you use would depend on how you've stored (or are getting the vals) but I hope you get the idea. You should also then remove the throws JSONException in your method declaration.

JSONObject when element doesn't exist

I have a struts action receiving following JSON:
{
"commandId":"tC",
"id":"123",
"def":""
}
Following code works just fine:
JSONObject command = null;
String commandId = null;
String deviceId = null;
try {
command = new JSONObject(json);
commandId = command.getString("commandId");
}
Since "def" can be empty, non declared or can contain another array of elements I tried doing this:
JSONObject def = command.getJSONObject("def");
in order to get this JSON object defined in the element def
This only works if def isn't empty like in this example:
{
"commandId":"tC",
"id":"123",
"def":{"1":"aaa", "2":"bbb"}
}
When def is empty or not defined my program stops working on the line JSONObject def = command.getJSONObject("def"); and noticed that it doesn't continue the execution?!
If I put JSONObject def = command.getJSONObject("def"); try / catch block I get _JSONObject["def"] is not a JSONObject _ exception, but execution doesn't continue
How does JSONObject.getJsonObject(String) behave?
I would expect it to return an empty JSONObject and continue the execution.
What I want is to check if there is anything defined in def and then in a if, else decide what to do in my program according to the value found there... I can't find a way to make my program work if a client's json comes with def empty or not defined.
my suggestion is to define "def" either be defined as null or {}:
"def":null or "def":{} to align with its usage agreement
quotes is really just used to indicate the value is a string. following the standard might save you and others from confusion in the future.
Likely it is because it is trying to get a Object and finding a string. In your JSON (if you control it), for an empty object I would do {}. This should allow Java to think it is retrieving an object.
If def is intended to be an object is it not suppose to look like this when empty?
{
"commandId":"tC",
"id":"123",
"def":{}
}
I think having "def":"" will cause the value to be attempted to be parsed as a string value and not an object value.
Maybe this will help someone. I had to solve the same problem. In my case the web service was returning empty JSON objects if it couldn't find the requested record.
Note: the data names have been changed to protect the innocent...
Note 2: this example uses javax.json
import javax.json.*;
JsonObject _jObj = _myRootObj.getJsonObject("someDataNode");
// at this point in execution _jObj could equal {"DataReturn":""}
// or {"DataReturn":"<some valid data>"}
// we want to test the existence of DataReturn before trying to
// use it
JsonValue jv = _jObj.getOrDefault("DataReturn", null);
String js = jv.toString();
// cover all the bases. test the value
if (js == null || js.isEmpty() || js.compareTo("\"\"") == 0)
{
throw new Exception("Error: DataReturn object null. Cannot proceed.");
}
// the object exists, so it's ok to get it
JsonObject jObjDate = _jObj.getJsonObject("DataReturn");
If you're using org.json.JSONObject you can use .isNull(String key) to do this, something like:
if (command.isNull("def") {
// Handle 'def' not being there
} else {
JSONObject def = command.getJSONObject("def");
}

Recursively reading any java Object and pulling out complex types into a hash map

I need to write a utility program which would accept a blank HashMap and any object as arguments and return the HashMap
public HashMap returnMap(HashMap map,Object parseThisObject){
//logic to strip all children, children of children...... and place it in HashMap
//return map
}
This object contains a lot of objects within it and those objects within has a lot of children and the lineage goes on.
My utility must be generic enough to recursively read through all children until it hits primitives in each object, place each of those objects in the hasp map and return it back.
This is something like the parent would be there in the map. but the individual children also would be there as sub sequent entries in the map.
I am new to java reflection and I went through some tutorials and examples on the net. Not very confident about how to proceed. I believe this is one of the frequent requirements experts and professionals over here might have faced.
Plese help me with a starting point on this. If there is any bean utilities open source available to do this? if so please let me know.
Something like this should do it:
public void fillMap(HashMap<String, Object> map, Object bean) {
try {
BeanInfo info = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] props = info.getPropertyDescriptors();
for (int i = 0; i < props.length; i++) {
Method reader = props[i].getReadMethod();
if (reader != null && !props[i].getName().equals("class")) {
Object invoke = reader.invoke(bean, new Object[] {});
if (invoke != null) {
if (!reader.getReturnType().isPrimitive()) {
fillMap(map, invoke);
} else {
map.put(props[i].getName(), invoke);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
this of course puts all fields from all objects into one map. You might have to create a map for each recursion step if you want submaps for children. I can give you that code as well if you need it.
There is no return of a map, because the one that is passed to the method is filled.
Have a look at Apache Commons BeanUtils. It already does a large chunk of what you need, and may even do the whole lot in one go.

Categories

Resources