3

In my current development task, I want to validate whether a string is in JSON format or not. When I checked for solutions from resources like StackOverflow I saw many of answers are as below,

try{
   JsonParser parser = new JsonParser();
   parser.parse(passed_json_string);
} 
catch(JsonSyntaxException jse){
  System.out.println("Not a valid Json String:"+jse.getMessage());
}

According to my knowledge, we are using try-catch to avoid terminating the program from exceptions.

According to the above solutions, I feel like I'm going to generate exceptions and from that and then validate my string.

Furthermore, the program pseudocode below is better than using a try-catch block,

if(string is JSON){
 // do this
}else{

// do this

}

Based on comments

Actually, my question is like this

Let's assume there is a zero divide problem

if(input > 0){
 result = number/input
}else{
print("inplut not valid")
}

or

try{
 result = number/input
}catch(){
print("input is not valid")
}

my question: which one is good practice?

please correct me if I'm wrong. thanks in advance

gnasher729
  • 42,090
  • 4
  • 59
  • 119
  • 1
    What does `string is JSON` do? Probably a function that tries to parse the string, catches an exception and returning false if parsing fails. – Rik D Dec 31 '21 at 09:16
  • @RikD my question is which one is good practice using try-catch or if-else, or there is nothing to worry and my understand is wrong – Dulanga Heshan Dec 31 '21 at 09:19
  • zero divide problem -> if(input > o){ number/input } or try(number/input){ } catch{} – Dulanga Heshan Dec 31 '21 at 09:22
  • @DulangaHeshan Which is good practice may depend on the details. If `string is JSON` does attempt to parse the string, doing something like `if (string is JSON) { parser.parse(string) }` is duplicating work and therefore probably bad if this is anywhere near your hot path. If `string is JSON` works some other way, it could be a good idea. Please include the details in the question as to what `string is JSON` does. – Philip Kendall Dec 31 '21 at 09:29
  • 2
    Be pragmatic about it. When parsing JSON, working with exceptions is likely the easiest way. Dividing by zero depends; by default I would check if input != 0, but if input should never be 0, throwing an exception is fine because then it’s an exceptional situation. – Rik D Dec 31 '21 at 09:30
  • @PhilipKendall I have updated my question thank you – Dulanga Heshan Dec 31 '21 at 09:37
  • While [this question](https://softwareengineering.stackexchange.com/questions/231057/exceptions-why-throw-early-why-catch-late) is not an exact duplicate, it might answer many questions around working with exceptions. – Rik D Dec 31 '21 at 09:52
  • @DulangaHeshan Please don't change the question; the problem you're trying to solve is validating a JSON string, not a made-up one about division by zero. – Philip Kendall Dec 31 '21 at 10:22
  • If the API you are using, may throw exceptions in expected use cases (like here malformed or missing JSON) you must handle it so your code behaves as expected. – Thorbjørn Ravn Andersen Jan 02 '22 at 00:25

3 Answers3

11

The problem here is most probably that your JsonParser does neither have a method isJson, nor does it have a method tryParse (which could avoid the duplicate work which would be involved in first testing for "parsability", and then parse again to get the actual result). So I guess you have no other option than catching the exception. But that's not "wrong" or "bad". Assumed getting a non-valid Json string is an "exceptional" situation (and not the regular case) this looks pretty natural.

In case your JsonParser would provide a tryParse method, it might be a good idea to use it instead of try/catch, since the resulting code would be a little bit shorter. However, to some degree this is still a matter of taste.

One issue with your second example is that in certain programming languages for certain numeric types a "division-by-zero" does not necessarily throw an exception. Some languages have a number representation "Infinity", at least for floating point math.

But let's assume this to be a case where an exception would be thrown, and input be an integer value, not a floating point. Then we see another issue here: the test if(input > 0) is not semantically equivalent to the implicit test done in the try/catch variant (which is if(input != 0)). So the if/else construct allows you to place a different, more strict validation into a single line. If that's what you want or need, the if/else is obviously the more correct choice.

Another thing might be performance: in lots of languages throwing an exception might be more costly than making an explicit test beforehand. However, in cases where a zero denominator occurs seldom, this might be negligible. So in case

  • you want a test for the denominator to be unequal to zero

  • the performance hit of the exception is negligible

it boils down to a matter of taste which construct to use. Note the try/catch might be beneficial in cases where the numerical calculations are more complex and you would have to check more than just one denominator, or multiple lines for validity (assumed you don't want individual treatment for different error situations).

So in short: there is no braindead "one size fits all solution", you have to look at the real individual case, the language, the provided features, the specific requirement, and not at some sloppily written pseudo-code out of context. Make yourself aware of the semantical differences and the performance differences, and then decide which elements to use.

See also: Arguments for or against using Try/Catch as logical operators

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • In general I agree with you in 99.999% but I would say, if there is a chance for TryXXX pattern (i.e. test beforehand) use it always instead of throwing+catching exception simply because of the performance. And even if some case performance is negligible I would still stick to the pattern, because (a) there is no harm (b) and it is a pattern. Ignoring a pattern means somebody else (me including) 5 years later will wonder why I ignored the pattern. Thus it would require adding comments, things get lengthy, and comments tend to be out-of-sync in time. Thus -- TryXXX pattern for me. – greenoldman Jan 01 '22 at 06:18
  • What if the divisor is not zero, but very very small. and the dividend is large? Overflow results. In general, you can't reasonably check for that beforehand. Well you could, by dividing and seeing if it blows up. :-) Just divide and check the result for an exception or a NaN. – user949300 Jan 02 '22 at 05:10
  • 1
    @user949300: assuming we are looking at a case where an exception is most sensible when "input" is an integer, not a float. But actually, your comment is supporting what I wrote: it makes not much sense to discuss this things out of context, when not even the number type is clear. – Doc Brown Jan 02 '22 at 11:15
1

If testing for proper input is simple, e.g. "is the input string non-null and non-empty", you should generally test the input.

If testing for proper input is difficult, then try / catch is perfectly fine. Imagine writing a regular expression for proper JSON. I think it's impossible - some Comp Sci Ph.D. could explain. Testing for a valid number that might be in scientific notation? Or a possible Date and Time? That is possible, but still a lot of work.

When testing for proper input format is this difficult, don't spend your time and effort on possibly buggy validation code - just try to parse it, with robust, well documented, and tested library code. And then deal with any thrown errors.

The question of where the catch should be is always challenging. It depends. But, unlike your (I know, contrived) code, the catch should almost never just print (or log) something and continue.

user949300
  • 8,679
  • 2
  • 26
  • 35
  • 1
    "imagine writing a regular expression for proper JSON" if you pick the wrong tool for the job no wonder you cannot achieve the goal. JSONs are not processed using regexes no matter try/catch or not. – greenoldman Jan 02 '22 at 06:27
-3

It's not a great use of try catch.

If you are using exceptions you want to allow them to bubble up to the top layer of the program, where you can handle them in a generic handler to output an error message or log.

ie

MyProgram
{
    Run()
    {
       try
       {
          //program loop
       }
       catch(e)
       {
          console.WriteLine("Error: " + e.Message);
       }
    }
}

For validation you really want to have a boolean function to call.

bool ValidateJson(string json);

or better yet a list of errors

List<ValidationError> ValidateJson(string json);

so you can do conditional logic

   if(ValidateJson(json))
   {
       //parse json and do stuff
   }
   else
   {
       //prompt user to fix json rather than erroring the entire program
   }

Obviously you can use try catch for conditional logic, but its less efficient and not as nice.

Ewan
  • 70,664
  • 5
  • 76
  • 161
  • 3
    You don't always want them to bubble up to the top. It depends on whether the call site can handle the issue or not. – siride Dec 31 '21 at 13:24
  • bubbling up is kind of the point of exceptions though. Thats why it's considered an antipattern if you are using them for conditionals. Sure its slow as well, but its more that you aren't following the pattern they are designed for – Ewan Dec 31 '21 at 15:59
  • My point isn't that they shouldn't bubble up, but rather that they should bubble up only to where they need to. Your advice is good in that it prevents people from swallowing exceptions at a low level. However, like any rules of thumb, there are exceptions (no pun intended) and if you don't understand the purpose of the rule, you'll miss those. If you can handle an exception locally (not just logging), then you absolutely should. But if you can't, it's okay to let it bubble up. – siride Dec 31 '21 at 16:15
  • 1
    In the case of JSON parsing, the code that calls the parser can probably handle the exception. It can package it into a more general error, or it can use a fallback, or even return null (maybe we don't care if the JSON parses -- it's just a best effort). The other thing about JSON parsing is that it can be computationally expensive and the error may be deeply nested in the parsing process. Exceptions are ideal here as they short circuit the process. Also, you don't want to try to parse twice (once to check validity, the second to convert to data structures). – siride Dec 31 '21 at 16:17
  • Thus, the best approach here is to try to parse and if that fails, catch the exception (or let it bubble, if that makes sense), rather than checking validity first. This is similar to patterns for file operations. Don't check if file doesn't exist before trying to create it, because it could be created in between the check and the creation. Just try to create it and then handle exception (which might entail trying a new filename, or whatever). – siride Dec 31 '21 at 16:18
  • I have to disagree. If you can handle it locally you don't need an exception and should use a boolean as in my example. Catching the exception is incorrect. – Ewan Dec 31 '21 at 17:32
  • as is upvoted 50 odd times in the post doc brown links to, but thats stackexchange for you – Ewan Dec 31 '21 at 17:33
  • Okay, if you wrote your own JSON parser, and you had a language where you could return a Result<>/Option<> type value (because a boolean false is a valid parsed JSON), and you were willing to write your parser such that it could fail in a deeply nested place and return failure all the way up the tree, then sure, a result check during parsing would be better. But I haven't seen JSON libraries that do that, so you kind of have no choice. – siride Dec 31 '21 at 17:52
  • you can use IgnorErrors in Json.net – Ewan Dec 31 '21 at 19:12
  • But yes, if you are writing an app where invaild json is common you would want to parse fragaments and highlight errors rather than a simple "is this whole thing vaild or have i missed a } somewhere?" – Ewan Dec 31 '21 at 19:14