Dealing with Exceptions and Errors where errors are OK - java

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.

Related

Continue the flow after throwing exception

In my use case, I am looping across a map and checking whether a particular key is present in a list. If it is present then I have to trow and exception otherwise continue with the execution.
Map<A,B> myMap = new HashMap<A,B>();
//code to populate values in myMap
...
...
List<A> myList = new ArrayList<A>();
//code to populate values in myList
...
...
for(Map.Entry<A,B> eachElementInMap:myMap.entrySet()){
if(myList.contains(eachElementInMap:myMap.getKey())){
//throwing exception
throw new MyCustomizedException("someString");
}
}
//code continues
...
....
In the above example, if there are 3 elements in the map(myMap) in which 1 key is present in the list(myList), I want to throw the exception for one and it should continue executing other lines of code for the rest two. Am I using a wrong design to achieve this? Any help or suggestion is appreciated! Thanks
Typically once you throw an exception, you are saying that the current line of execution should terminate, rather than continue. If you want to keep executing code, then maybe hold off on throwing an exception.
boolean fail = false;
for (Map.Entry<A,B> eachElementInMap:myMap.entrySet()) {
if (myList.contains(eachElementInMap:myMap.getKey())) {
// throw an exception later
fail = true;
}
}
if (fail) {
throw new MyCustomizedException("someString");
}
You can also create an exception object at a different location from where you throw it. This idiom will be useful in cases where the exception message is not just "someString", but needs to be constructed from data obtained from the object being iterated over.
Optional<MyCustomizedException> exception = Optional.empty();
for (Map.Entry<A, B> eachElementInMap:myMap.entrySet()) {
if (myList.contains(eachElementInMap.getKey())) {
// Create an exception object that describes e.g., the missing key(s)
// but do not throw it yet.
if( exception.isPresent() ) {
exception.get().addToDescription( /* Context-sensitive information */ );
}
else {
exception = Optional.of(
new MyCustomizedException( /* Context-sensitive information */));
}
}
}
if( exception.isPresent() ) {
throw exception.get();
}
If the only data stored in the exception is a string, an equivalent effect can be achieved by accumulating problem descriptions in a StringBuilder, but for cases where more interesting data needs to go into the exception object, building as you go might be an option worth considering.
You can split it into two lists,failList and successList. and do it.
This is clearer
failList = myMap.entrySet().stream().filter(p->myList.contains(p.getKey())).collect(Collectors.toList());
successList = myMap.entrySet().stream().filter(p->!myList.contains(p.getKey())).collect(Collectors.toList());
failList.forEach(p -> {
// fail code
});
successList .forEach(p -> {
// success code
});
why not use if...else instead of try catch ? error just means that's a mistake. if you afraid that makes some mistakes what you don't know. you can use throw error.
anyway, it should not be used when the program is running as you wish

Java string comparison fails

I've been looking for a mistake in my code for hours now but I simply can't find it.
I could locate that the mistake is in this part of my code:
public String getType(File file)
{
String type = null;
try
{
type = URLConnection.guessContentTypeFromName(file.getAbsolutePath());
if (type.startsWith("image"))
{
Log.d(logTag, file.getAbsolutePath());
}
}
catch (Exception e){}
return type;
}
This works absolutely fine but when I do the string comparison directly before the return statement I don't get all types which start with "image".
The same thing happens when I don't use the exception handling block.
I've also displayed the integer values of the string and they do equal.
In case it matters: This is an Android project.
Edit:
E.g I get
/storage/emulated/0/WhatsApp/Media/WhatsApp Images/IMG-20150906-WA0000.jpg
in both cases whereas I receive
/storage/emulated/0/Profilbilder/18186367897.jpg
only when I use the code above. And no Exception is thrown for this file.
Edit:
I created a new project and it works as expected now. Since my new code is not exactly the same but almost, I can't tell whether it was a logical mistake or the compiler
When file is null or URLConnection throws some exception, type must be null, then there is a nullpointerException on type.startsWith("image")

Flattening a JSONObject in Java - Recursion causing StackOverflowError

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);

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");
}

Better way to catch trouble points

User submits a CSV file which is consumed by a program. Values which are used throughout the program come from the CSV, natually if values are missed it is a problem. Below is my solution.
Ip on top
private List<String> currentFieldName = new ArrayList<String>();
As part of the method:
try {
setCurrentFieldName("Trim Space");
p.setTrimSpace(currentLineArray[dc.getTRIM_POSITION()].equals("yes") ? true : false);
setCurrentFieldName("Ignore Case");
p.setIgnoreCase(currentLineArray[dc.getIGNORE_CASE_POSITION()].equals("yes") ? true : false);
} catch (NullPointerException e) {
throw new InputSpreadsheetValueUnassignedException("\"Type\" field not set: " + currentFieldName);
}
And the method which keeps track of a current field being looked at:
private void setCurrentFieldName(String fieldName) {
currentFieldName.clear();
currentFieldName.add(fieldName);
}
The idea there is that if user fails to submit value and i will end up getting null, before throwing an exception, i will know what value was not assigned.
So, this being said, specific questions:
Is what i have shown below an acceptable solution?
Can you suggest something more elegant?
First thing that comes to my mind is that using an ArrayList to represent the name of a single field is superfluous.
Why not just define a private String currentFieldName; and inside your try { } do currentFieldName = "Trim Space" etc?
Also,
p.setTrimSpace(currentLineArray[index].equals("yes") ? true : false);
can just as well be expressed as
p.setTrimSpace(currentLineArray[index].equals("yes"));
If your code goes through many columns, you could definitely make it more elegant. If not, your time might be better spent on other parts of your project.
The answer to whether or not your solution is acceptable depends on the requirements, and a test suite would be the ideal party to provide the yes or the no.

Categories

Resources