【问题标题】:How to avoid JSONObject Null check如何避免 JSONObject Null 检查
【发布时间】:2019-09-27 07:31:56
【问题描述】:

我有如下 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 有任何“优点”,您也可以分享一下吗?

【问题讨论】:

  • 这是什么语言?这是 Java 吗?
  • @Amy 是的,它是 Java

标签: java json null


【解决方案1】:

选项 0:长 If 语句

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

选项 1:Try-Catch

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

选项 2:Java 8 可选

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

选项 3:路径表达式

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

【讨论】:

  • 感谢您的回答。如果我对 json 对象本身进行空检查,我认为 JSONException 应该捕获我在 if 条件中检查的所有可能的问题。请分享你的看法。我会检查 2 和 3 - 谢谢分享。
  • @ctimus:我认为选项 3 是迄今为止最优雅的选项,我更新了答案。
  • 我尝试了选项 3,如下所示: String a1 = JsonPath.read(document, "$.c.f[0].a");但是我仍然需要将此语句包含在不处理 ClassCastException 的 PathNotFoundException 中。然而 JSONException 捕捉到了这一点并显示了正确的消息。我仍然觉得,对于我所描述的场景,只有捕获 JSONException 才能捕获所有我在 if 条件下要解决的问题。如果可能发生任何其他异常,那么在 long-if 选项中也会发生这种情况。
  • 我已经尝试了带有 Option.SUPPRESS_EXCEPTIONS 的 json 路径,它正在做我想做的事情,但是(1)与使用选项 0 和 1 相比,这需要很多时间(2)它显示了一个日志 DEBUG com .jayway.jsonpath.internal.path.CompiledPath - 评估每次读取的路径 - 不确定是否需要花费时间。所以 JSONPath 似乎没有那么有效。我需要做些什么不同的事情吗?
  • @ctimus:不客气!如果您的问题已得到解答,请确保接受答案以供进一步参考。
【解决方案2】:

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

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到 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()

【讨论】:

    【解决方案3】:

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

    这里有几个原因:

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

    • 如果您登录NullPointerException 为:

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

      可能无法判断哪些组件丢失或为空。堆栈跟踪中的行号不足以区分这些情况。 (带有JSONException的异常信息会给你更多的线索。)

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

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


    如果在这种情况下使用 JSONException 有任何“优点”,您也可以分享一下吗?

    唯一真正的“优点”是它可能需要更少的代码,尤其是如果您可以将 try ... catch 放在很多这样的代码周围。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多