如何避免 JSONObject 空检查



我有如下所示的JSON

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

我正在将文件读入字符串并创建一个 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在这种情况下是否有任何"优点"吗?

选项 0:长 if 语句

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

选项 1:尝试捕获

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

选项 2:Java 8 可选

另一种选择是查找允许您使用 Java 8 Optional 类型的库。例如,建议使用杰克逊。这更优雅,但也可能成为一个大链条。此外,这会为您的项目添加另一个依赖项。

选项 3:路径表达式

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

由于上面的回复,以下是问题的解决方式。

JSONPath 在我提到的场景中效果最好。多亏了@Konrad。

首先创建一个类型为 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 转换为字符串,从而提供最佳性能。
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()

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

以下是几个原因:

  • 效率:创建、抛出和捕获异常相对昂贵。 确切地说,版本依赖于版本,也可能取决于上下文。 在最新版本中,JIT 编译器可以 (AFAIK) 将某些序列优化为条件分支。 但是,如果您要记录异常,那么JVM将不得不创建一个异常对象并填充堆栈跟踪,这是最昂贵的部分。

  • 如果您登录NullPointerException

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

    可能无法判断缺少哪些组件或为空。 堆栈跟踪中的行号不足以区分案例。 (JSONException异常消息将为您提供更多线索。

  • 你也无法区分jsonnull的情况,这可能是一种不同类型的问题;即一个错误。 如果您将其视为数据错误,则很难找到和修复代码错误。

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


您还可以分享使用JSONException在这种情况下是否有任何"优点"吗?

唯一真正的"优点"是它可能更少的代码,特别是如果你可以尝试......抓住很多这样的代码。

最重要的是,您需要自己权衡这一点。 一个因素是你得到与代码预期不匹配的JSON的可能性有多大。 这将部分取决于产生JSON的原因。

相关内容

  • 没有找到相关文章

最新更新