如何在反序列化映射失败时使Jackson抛出异常

asi*_*swt 8 java json jackson deserialization

杰克逊在处理反序列化映射过程中出现的异常怪异的行为:它抛出一个JsonMappingException,其.getCause()返回异常链的最里面.

//in main
ObjectMapper jsonMapper = new ObjectMapper();
String json = "{\"id\": 1}";
try {
    Q q = jsonMapper.readValue(json, Q.class);
} catch (JsonMappingException e) {
    System.out.println(e.getCause()); //java.lang.RuntimeException: ex 2
}

//class Q
public class Q {
    @JsonCreator
    public Q(@JsonProperty("id") int id) {
        throw new RuntimeException("ex 0", 
            new RuntimeException("ex 1", 
                new RuntimeException("ex 2")));
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我使用jsonMapper.readValue(..)将String映射json到类的实例,Q其构造函数标记为@JsonCreator抛出一个链RuntimeException:"ex 0", "ex 1", "ex 2".当映射失败时,我预计该行将System.out.println(e.getCause());打印出来ex 0,但它会打印出来ex 2.

为什么杰克逊决定这样做,有没有办法配置它,以便它不丢弃我的ex 0?我试过了

jsonMapper.configure(DeserializationFeature.WRAP_EXCEPTIONS, false);
Run Code Online (Sandbox Code Playgroud)

但它没有做任何事情.

Sam*_*rry 6

StdValueInstantiator在反序列化期间抛出异常时,Jackson的内部会触发此方法:

protected JsonMappingException wrapException(Throwable t)
{
    while (t.getCause() != null) {
        t = t.getCause();
    }
    if (t instanceof JsonMappingException) {
        return (JsonMappingException) t;
    }
    return new JsonMappingException("Instantiation of "+getValueTypeDesc()+" value failed: "+t.getMessage(), t);
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这将迭代嵌套运行时异常的每个"级别",并设置它命中的最后一个作为JsonMappingException它返回的原因.

以下是我需要的代码:

  1. 将新模块注册到ObjectMapper.

    @Test
    public void testJackson() {
        ObjectMapper jsonMapper = new ObjectMapper();
        jsonMapper.registerModule(new MyModule(jsonMapper.getDeserializationConfig()));
        String json = "{\"id\": \"1\"}";
        try {
            Q q = jsonMapper.readValue(json, Q.class);
            System.out.println(q.getId());
        } catch (JsonMappingException e) {
            System.out.println(e.getCause()); //java.lang.RuntimeException: ex 2
        } catch (JsonParseException e) {
        } catch (IOException e) {
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建自定义模块类.

    public class MyModule extends SimpleModule {
        public MyModule(DeserializationConfig deserializationConfig) {
            super("MyModule", ModuleVersion.instance.version());
            addValueInstantiator(Q.class, new MyValueInstantiator(deserializationConfig, Q.class));
            addDeserializer(Q.class, new CustomDeserializer());
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建ValueInstantiator要覆盖的自定义类wrapException(...).将实例化器添加到模块.

    public class MyValueInstantiator extends StdValueInstantiator {
        public MyValueInstantiator(DeserializationConfig config, Class<?> valueType) {
            super(config, valueType);
        }
    
        @Override
        protected JsonMappingException wrapException(Throwable t) {
            if (t instanceof JsonMappingException) {
                return (JsonMappingException) t;
            }
            return new JsonMappingException("Instantiation of "+getValueTypeDesc()+" value failed: "+t.getMessage(), t);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 创建自定义反序列化器以使模块正常工作.将此类添加到模块初始化中.

    public class CustomDeserializer extends StdScalarDeserializer<Q> {
        public CustomDeserializer() {
            super(Q.class);
        }
    
        @Override
        public Q deserialize(JsonParser jp, DeserializationContext context) throws IOException {
            JsonNode node = jp.getCodec().readTree(jp);
            return new Q(node.get("id").asText());
        }
    
        @Override
        public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
            return deserialize(jp, ctxt);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)