杰克逊和那个可怕的IOException

wul*_*pro 12 java convention json exception jackson

杰克逊的 ObjectMapper#readValue成员抛出三个例外:

IOException 
JsonParseException 
JsonMappingException
Run Code Online (Sandbox Code Playgroud)

JsonParseExceptionJsonMappingException扩展IOException.我想包装前面提到的两个子类并抛出我自己的自定义异常,然而IOException,被检查的基类要求我捕获或抛出它.

我把它扔到IOException调用层是没有意义的,但是,如果我把它隐藏起来,那就是一种气味.我最初的想法是不抓住它并将其留给调用者/运行时异常机制来处理它......但是,我不想强​​迫调用者捕获或指定.

在这种情况下,人们会怎么做?

小智 12

简短的回答:如果你处理IO,你就应对IOExceptions.如果你不处理IO,那么IOExceptions应该变成未经检查的异常,因为它们是错误代码的症状.


更长的回答:

readValue总是需要一个JsonParser,它可以包裹在IO(例如文件或URL).如果您正在处理IO,则无法处理IOExceptions,您应该处理它们或以某种方式重新抛出/传递它们.IO期间可能发生任何事情,您应该准备好处理异常.

但是,如果您确定您的JsonParser实例不使用IO(例如,您曾经JsonFactory#createJsonParser(java.lang.String)在字符串上创建JSON解析器),您可能会认为IOException您收到的任何内容都是错误,无论是在您的代码中还是在Jackson中.通常,抛出未经检查的异常是处理它的正确方法:

ObjectMapper om = new ObjectMapper(/* whatever */);
JsonParser jp = JsonFactory.createJsonParser("{ \"foo\": \"bar\" }");
try {
    return om.readValue(jp);
} catch (IOException e) {
    throw new AssertionError("An IOException occurred when this was assumed to be impossible.");
}
Run Code Online (Sandbox Code Playgroud)

Nota bene:我的Java生锈了,我从未使用过Jackson,所以请考虑上面的块是伪代码.

在任何情况下,你永远需要声明AssertionErrorthrows,因为他们是unchecked异常.所有的子类都是java.lang.RuntimeException或者java.lang.Error不需要被明确地捕获或重新抛出.这些异常用于预计不会发生的问题,除非您正在处理错误的代码或VM的主机受到攻击.

  • +1:我喜欢这样的想法,即您要么必须处理异常,要么通过包装在 RuntimeException 中“声明它不可能”。 (2认同)
  • @Trinctorius - _"关于何时抛出每种类型,基本规则是 Jackson 尝试从读取源(InputStream、Reader)传递底层 IOExceptions,并且在有限数量的情况下,问题与低级字符解码(无效UTF-8 序列)。”_ 我将一个 `String` 传递给 `ObjectMapper#readValue`。从我的角度来看,没有 IO 真正发生。 (2认同)