简体   繁体   English

如何避免 JSONObject Null 检查

[英]How to avoid JSONObject Null check

I have JSON like below我有 JSON 如下所示

{
  "a1": "aaa",
  "b1": 333,
  "c1": {
    "c1": "ccc",
    "d1": "ddd",
    "f1": [
      {"a1": "xyz"},
      {"b1":  "lmn"},
      {"c1":123.00}
    ]
  }
}

I am reading the file into String and creating a JSONObject as below我正在将文件读入 String 并创建一个 JSONObject 如下

JSONObject json = new JSONObject(new JSONTokener(str));

The JSON is coming to my application from outside so the content can be very different in times. JSON 从外部进入我的应用程序,因此内容在时间上可能会有很大不同。 Say it can be completely null or some of the elements can be null or array size can be 0 or 1 or more etc.说它可以完全是 null 或者某些元素可以是 null 或者数组大小可以是 0 或 1 或更多等。

When I am working on the JSONObject I can keep on checking all the elements using当我处理 JSONObject 时,我可以继续使用检查所有元素

json.has and !=null 

so that it does not throw any exception.这样它就不会抛出任何异常。

I can have the code as below我可以有如下代码

if(
      json.has("c") 
   && json.getJSONObject("c")!= null 
   && json.getJSONObject("c").has("f") 
   && json.getJSONObject("c").getJSONArray("f").length() > 1 
   &&json.getJSONObject("c").getJSONArray("f").getJSONObject(1).has("b")
  ){     
      String x = json.getJSONObject("c").getJSONArray("f").getJSONObject(1).getString("b");
   }

That makes the code having a long list of if conditions.这使得代码具有一长串 if 条件。

However I am thinking instead I can just enclose the statement with try catch但是我在想,我可以用 try catch 来附上语句

try {
     String x = json.getJSONObject("c").getJSONArray("f").getJSONObject(1).getString("b");
    }catch(JSONException e) {
        //log and proceed
    }

Please suggest if there is any valid reason in this case to put a long if conditions rather than just try - catch - log and proceed.请建议在这种情况下是否有任何正当理由放置一个较长的 if 条件,而不是仅仅尝试 - 捕获 - 记录并继续。

Also can you share if using JSONException has any "pros" in this context?如果在这种情况下使用 JSONException 有任何“优点”,你也可以分享吗?

Option 0: Long If-Statement选项 0:长 If 语句

I think this is far too convoluted, especially as the chain gets longer.我认为这太令人费解了,尤其是当链条变长时。 The only case where I would consider a remotely similar solution is if the user needs to know exactly at which point the problem lies and there are specific guidelines on how to solve this and how this may occur in a very specific use case, however in that case you need a large number of if statements and log statements.我会考虑远程类似解决方案的唯一情况是,如果用户需要确切地知道问题出在哪一点,并且有关于如何解决这个问题以及在非常具体的用例中如何发生这种情况的具体指导方针,但是在那个如果你需要大量的 if 语句和 log 语句。

Option 1: Try-Catch选项 1:Try-Catch

Try-catch, as you suggested, is indeed possible, however you need to make sure that you catch all possible exceptions that can occur when a JSON field is not present or of the wrong type, for example ClassCastException and NullPointerException.正如您所建议的,Try-catch 确实是可能的,但是您需要确保捕获在 JSON 字段不存在或类型错误时可能发生的所有异常,例如 ClassCastException 和 NullPointerException。 It is short but not very elegant and as the other answerer said might hide other exceptions (but you could still log the stack trace).它很短但不是很优雅,正如其他回答者所说可能隐藏其他异常(但您仍然可以记录堆栈跟踪)。

Option 2: Java 8 Optional选项2:Java 8 可选

Another option is to find a library that allows you to use the Java 8 Optional type.另一种选择是找到一个库,允许您使用 Java 8 可选类型。 For example, this is suggested using Jackson .例如,建议使用Jackson This is more elegant but could become a large chain as well.这更优雅,但也可以成为一个大链。 Also this adds one more dependency to your project.这也为您的项目增加了一个依赖项。

Option 3: Path Expressions选项 3:路径表达式

A third option would be to use path expression JsonPath , where you can put all your statements into one expression and get all results for that.第三种选择是使用路径表达式JsonPath ,您可以将所有语句放入一个表达式中并获得所有结果。 In my opinion this is the perfect use case and by far the best solution.在我看来,这是一个完美的用例,也是迄今为止最好的解决方案。 The only downside is that this adds one more dependency to your project.唯一的缺点是这为您的项目增加了一个依赖项。

Please suggest if there is any valid reason in this case to put a long if conditions rather than just try - catch - log and proceed.请建议在这种情况下是否有任何正当理由放置一个较长的 if 条件,而不是仅仅尝试 - 捕获 - 记录并继续。

Here are a couple of reasons:这里有几个原因:

  • Efficiency: creating, throwing and catching an exception is relatively expensive.效率:创建、抛出和捕获异常相对昂贵。 Exactly how expensive is version dependent, and probably context dependent too.究竟有多昂贵取决于版本,也可能取决于上下文。 In recent versions, the JIT compiler can (AFAIK) optimize some sequences into a conditional branch.在最近的版本中,JIT 编译器可以 (AFAIK) 将一些序列优化为条件分支。 However, if you are going to log the exception, then the JVM is going to have to create an exception object and populate the stacktrace, and that this the most expensive part.但是,如果您要记录异常,那么 JVM 将不得不创建异常 object 并填充堆栈跟踪,这是最昂贵的部分。

  • If you log NullPointerException for:如果您将NullPointerException记录为:

     json.getJSONObject("c").getJSONArray("f").getJSONObject(1).getString("b")

    you may not be able to tell which of the components was missing or null.可能无法分辨缺少哪些组件或 null。 The line number in the stacktrace won't be sufficient to distinguish the cases.堆栈跟踪中的行号不足以区分这些情况。 (With a JSONException the exception message will give you more clues.) (使用JSONException异常消息将为您提供更多线索。)

  • You also can't distinguish the case where json is null , which is probably a different kind of problem;您也无法区分jsonnull的情况,这可能是另一种问题; ie a bug.即一个错误。 If you treat that as a data error, you are making it difficult to find and fix the code bug.如果您将其视为数据错误,则很难找到和修复代码错误。

  • If you catch all exceptions (as suggested by another answer), you are potentially hiding more classes of bugs.如果您捕获所有异常(如另一个答案所建议的那样),您可能会隐藏更多类别的错误。 Bad idea.馊主意。


Also can you share if using JSONException has any "pros" in this context?如果在这种情况下使用 JSONException 有任何“优点”,你也可以分享吗?

The only real "pro" is that it is possibly less code, especially if you can put the try... catch around a lot of this code.唯一真正的“优点”是它可能需要更少的代码,特别是如果你可以尝试...捕获很多这样的代码。

The bottom line is that you need to weigh this up for yourself.最重要的是,您需要自己权衡一下。 And one factor is how likely it is that you will get JSON that doesn't match your code's expectations.一个因素是您获得不符合您的代码预期的 JSON 的可能性有多大。 That will depend in part on what is producing the JSON.这将部分取决于生产 JSON 的产品。

Thanks to the responses above, below is how the problem has been solved.感谢上面的回复,下面是问题的解决方法。

JSONPath is working the best for the scenario I mentioned. JSONPath最适合我提到的场景。 Thanks to @Konrad.感谢@Konrad。

First create a configuration object of type com.jayway.jsonpath.Configuration首先创建一个 com.jayway.jsonpath.Configuration 类型的配置object

Configuration conf = Configuration.builder().options(Option.SUPPRESS_EXCEPTIONS).mappingProvider(new JsonOrgMappingProvider()).jsonProvider(new JsonOrgJsonProvider()).build();
  • Option.SUPPRESS_EXCEPTIONS - This will help to suppress the exception in case an element is missing Option.SUPPRESS_EXCEPTIONS - 这将有助于在缺少元素的情况下抑制异常
  • mappingProvider(new JsonOrgMappingProvider()).jsonProvider(new JsonOrgJsonProvider() - This uses the right provider so that when we parse the json we don't need to convert the JSONObject to String thus giving optimal performance. mappingProvider(new JsonOrgMappingProvider()).jsonProvider(new JsonOrgJsonProvider() - 这使用了正确的提供程序,因此当我们解析 json 时,我们不需要将 JSONObject 转换为 String 从而提供最佳性能。
DocumentContext docContext = JsonPath.using(conf).parse(json);

If we use default provider then we need to parse the JSONObject as follows如果我们使用默认提供程序,那么我们需要按如下方式解析 JSONObject

DocumentContext docContext = JsonPath.using(conf).parse(json.toString());

Then to read the element I used然后阅读我使用的元素

docContext.read("$.a.b.c.d.values[0].e.f")

The performance is now optimal as well.性能现在也是最佳的。 The reason it was taking more time and memory was because花费更多时间和 memory 的原因是因为

  • I was reading and parsing at the same time in a loop.我在一个循环中同时阅读和解析。 Later on I moved the parsing out of loop.后来我将解析移出循环。
  • I was using default provider and was doing json.toString()我正在使用默认提供程序并且正在执行 json.toString()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM