为了账号安全,请及时绑定邮箱和手机立即绑定

如何避免 JSONObject Null 检查

如何避免 JSONObject Null 检查

www说 2023-09-06 16:44:33
我有像下面这样的 JSON{  "a1": "aaa",  "b1": 333,  "c1": {    "c1": "ccc",    "d1": "ddd",    "f1": [      {"a1": "xyz"},      {"b1":  "lmn"},      {"c1":123.00}    ]  }}我正在将文件读入 String 并创建一个 JSONObject ,如下所示JSONObject json = new JSONObject(new JSONTokener(str));JSON 是从外部传入我的应用程序的,因此内容有时可能会非常不同。假设它可以完全为空,或者某些元素可以为空,或者数组大小可以为 0 或 1 或更多等等。当我处理 JSONObject 时,我可以使用以下命令继续检查所有元素json.has and !=null 这样它就不会抛出任何异常。我可以得到如下代码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");   }这使得代码具有一长串 if 条件。不过我想我可以用 try catch 将语句括起来try {     String x = json.getJSONObject("c").getJSONArray("f").getJSONObject(1).getString("b");    }catch(JSONException e) {        //log and proceed    }请建议在这种情况下是否有任何有效的理由来放置很长的 if 条件,而不是仅仅尝试 - 捕获 - 记录并继续。您还可以分享一下使用 JSONException 在这种情况下有什么“优点”吗?
查看完整描述

3 回答

?
饮歌长啸

TA贡献1951条经验 获得超3个赞

选项 0:长 If 语句

我认为这太复杂了,尤其是当链条变得更长时。我会考虑使用远程类似解决方案的唯一情况是,如果用户需要确切地知道问题出在哪里,并且有关于如何解决这个问题以及在非常特定的用例中如何发生这种情况的具体指南,但是在这种情况下如果您需要大量的 if 语句和 log 语句。

选项 1:尝试捕获

正如您所建议的那样,Try-catch 确实是可能的,但是您需要确保捕获当 JSON 字段不存在或类型错误时可能发生的所有可能的异常,例如 ClassCastException 和 NullPointerException。它很短但不是很优雅,正如其他回答者所说可能隐藏其他异常(但您仍然可以记录堆栈跟踪)。

选项 2:Java 8 可选

另一种选择是找到一个允许您使用 Java 8 可选类型的库。例如,建议使用Jackson。这更优雅,但也可以成为一个大型连锁店。这也为您的项目增加了一种依赖。

选项 3:路径表达式

第三种选择是使用路径表达式JsonPath,您可以将所有语句放入一个表达式中并获取该表达式的所有结果。在我看来,这是完美的用例,也是迄今为止最好的解决方案。唯一的缺点是这会给您的项目增加一个依赖项。


查看完整回答
反对 回复 2023-09-06
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

请建议在这种情况下是否有任何有效的理由来放置很长的 if 条件,而不是仅仅尝试 - 捕获 - 记录并继续。

以下是几个原因:

  • 效率:创建、抛出和捕获异常的成本相对较高。到底有多昂贵取决于版本,也可能取决于上下文。在最近的版本中,JIT 编译器可以(据我所知)将某些序列优化为条件分支。但是,如果您要记录异常,那么 JVM 将必须创建一个异常对象并填充堆栈跟踪,这是最昂贵的部分。

  • 如果您登录NullPointerException

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

    可能无法判断哪个组件丢失或为空。堆栈跟踪中的行号不足以区分情况。(JSONException异常消息将为您提供更多线索。)

  • 您也无法区分jsonis 的情况null,这可能是另一种问题;即一个错误。如果您将其视为数据错误,则很难找到并修复代码错误。

  • 如果您捕获所有异常(如另一个答案所建议的),则可能会隐藏更多类别的错误。馊主意。


您还可以分享一下使用 JSONException 在这种情况下有什么“优点”吗?

唯一真正的“优点”是它可能会减少代码,特别是如果您可以将try ... catch放在很多这样的代码中。

最重要的是,您需要自己权衡这一点。其中一个因素是您获得的 JSON 与代码预期不符的可能性有多大。这部分取决于生成 JSON 的内容。


查看完整回答
反对 回复 2023-09-06
?
斯蒂芬大帝

TA贡献1827条经验 获得超8个赞

感谢上面的回复,下面是问题的解决方法。

JSONPath对于我提到的场景效果最好。

首先创建一个com.jayway.jsonpath.Configuration类型的配置对象

Configuration conf = Configuration.builder().options(Option.SUPPRESS_EXCEPTIONS).mappingProvider(new JsonOrgMappingProvider()).jsonProvider(new JsonOrgJsonProvider()).build();
  • Option.SUPPRESS_EXCEPTIONS - 这将有助于在元素丢失时抑制异常

  • mappingProvider(new JsonOrgMappingProvider()).jsonProvider(new JsonOrgJsonProvider() - 这使用正确的提供程序,这样当我们解析 json 时,我们不需要将 JSONObject 转换为 String,从而提供最佳性能。

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

如果我们使用默认提供程序,那么我们需要如下解析 JSONObject

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

然后读取我使用的元素

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

现在性能也达到最佳状态。花费更多时间和内存的原因是

  • 我在循环中同时阅读和解析。后来我将解析移出了循环。

  • 我正在使用默认提供程序并正在执行 json.toString()


查看完整回答
反对 回复 2023-09-06
  • 3 回答
  • 0 关注
  • 117 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信