2012-01-31 45 views
13

我的问题很简单:我有以下简单的类:杰克逊反序列化的错误处理

public class Foo { 
    private int id = -1; 
    public void setId(int _id){ this.id = _id; } 
    public int getId(){ return this.id; } 
} 

,我试图以处理以下JSON:

{ 
    "id": "blah" 
} 

显然,这里有一个问题(“blah”不能被解析为int)

以前,Jackson抛出类似org.codehaus.jackson.map.JsonMappingException的东西:无法从String构造java.lang.Integer的实例值“胡说”:不是一个有效的整数值

我同意这一点,但我想注册某个地方的东西,允许忽略这种类型的映射错误的。 我尝试使用已注册的DeserializationProblemHandler(请参阅here),但似乎只适用于未知属性而不是反序列化问题。

你对这个问题有任何线索吗?

+0

为什么要忽略这个错误?我会给每个客户端返回一个'400'的HTTP代码,这个客户端试图给我这样一个资源表示方法:) – 2012-01-31 14:36:17

+1

我使用Jackson和Spring MVC和bean验证。 问题是杰克逊抱怨反序列化问题,在我到达春天mvc层之前..所以我不能以一致的方式向我的客户发送错误。 – 2012-01-31 14:46:30

+0

另外我(一个)经常使用杰克逊做一个可读的对象日志转储。能够记录序列化问题并继续前进非常有帮助 – 2017-02-03 18:22:41

回答

9

我成功解决了我的问题,这要感谢Tatu from Jackson ML

我不得不使用定制的非阻塞解串器在处理杰克逊每基本类型。 事情是这样的工厂:

public class JacksonNonBlockingObjectMapperFactory { 

    /** 
    * Deserializer that won't block if value parsing doesn't match with target type 
    * @param <T> Handled type 
    */ 
    private static class NonBlockingDeserializer<T> extends JsonDeserializer<T> { 
     private StdDeserializer<T> delegate; 

     public NonBlockingDeserializer(StdDeserializer<T> _delegate){ 
      this.delegate = _delegate; 
     } 

     @Override 
     public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
      try { 
       return delegate.deserialize(jp, ctxt); 
      }catch (JsonMappingException e){ 
       // If a JSON Mapping occurs, simply returning null instead of blocking things 
       return null; 
      } 
     } 
    } 

    private List<StdDeserializer> jsonDeserializers = new ArrayList<StdDeserializer>(); 

    public ObjectMapper createObjectMapper(){ 
     ObjectMapper objectMapper = new ObjectMapper(); 

     SimpleModule customJacksonModule = new SimpleModule("customJacksonModule", new Version(1, 0, 0, null)); 
     for(StdDeserializer jsonDeserializer : jsonDeserializers){ 
      // Wrapping given deserializers with NonBlockingDeserializer 
      customJacksonModule.addDeserializer(jsonDeserializer.getValueClass(), new NonBlockingDeserializer(jsonDeserializer)); 
     } 

     objectMapper.registerModule(customJacksonModule); 
     return objectMapper; 
    } 

    public JacksonNonBlockingObjectMapperFactory setJsonDeserializers(List<StdDeserializer> _jsonDeserializers){ 
     this.jsonDeserializers = _jsonDeserializers; 
     return this; 
    } 
} 

然后调用它像这样(传为解串器,只有那些你想成为非阻塞):

JacksonNonBlockingObjectMapperFactory factory = new JacksonNonBlockingObjectMapperFactory(); 
factory.setJsonDeserializers(Arrays.asList(new StdDeserializer[]{ 
    // StdDeserializer, here, comes from Jackson (org.codehaus.jackson.map.deser.StdDeserializer) 
    new StdDeserializer.ShortDeserializer(Short.class, null), 
    new StdDeserializer.IntegerDeserializer(Integer.class, null), 
    new StdDeserializer.CharacterDeserializer(Character.class, null), 
    new StdDeserializer.LongDeserializer(Long.class, null), 
    new StdDeserializer.FloatDeserializer(Float.class, null), 
    new StdDeserializer.DoubleDeserializer(Double.class, null), 
    new StdDeserializer.NumberDeserializer(), 
    new StdDeserializer.BigDecimalDeserializer(), 
    new StdDeserializer.BigIntegerDeserializer(), 
    new StdDeserializer.CalendarDeserializer() 
})); 
ObjectMapper om = factory.createObjectMapper(); 
6

你可能想要让你的控制器处理问题通过添加处理这个特定的异常

@ExceptionHandler(HttpMessageNotReadableException.class) 
@ResponseBody 
public String handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) 
{ 
    JsonMappingException jme = (JsonMappingException) ex.getCause(); 
    return jme.getPath().get(0).getFieldName() + " invalid"; 
} 

当然的方法,该线

JsonMappingException jme = (JsonMappingException) ex.getCause(); 

可能会抛出一些情况下,类转换异常,但我还没有遇到过他们呢。

+1

你说得对,这可能是一个有效的解决方案。 然而,它也有一些缺点: - 你必须报一个接一个(你将需要3个请求,以确定你有3名格式错误的字段)的错误。 把空值格式错误时,现场让我把\ @NotNull标注在球场上才能有详尽的错误列表1个请求 - 这允许的情况下,逐案处理上的每个领域。如果某些字段的格式不正确*和*不重要,我不会在这些字段上放置\ @NotNull =>错误将被过滤 – 2014-07-10 09:32:45

+0

是的,您说得对,您的方法更好。 我很感兴趣,你是如何使它为原始值工作的,因为我希望我的域模型具有**私人年龄**,而不是**私人整数年龄**。 我设法通过使用**新的StdDeserializer.IntegerDeserializer(int.class,0),**并用**返回delegate.getNullValue(); **替换** deserialize方法返回null **来解决此问题。如果您认为这是正确的解决方案,请更新您的代码以包含此内容,如果您有其他方法,请分享。 – 2014-08-02 09:19:21

+0

我想知道在这种情况下考虑原始类型是否一个好主意。因为使用一些值来表示“非法”值(在你的情况下,该值似乎为0)听起来很奇怪。我宁愿使用较少使用的值,例如Integer.MAX_VALUE,但即使在这种情况下,我觉得这种行为有点过于随意(这是一个人的POV) – 2014-08-03 21:36:28

0

创建一个简单的映射:

@Provider 
@Produces(MediaType.APPLICATION_JSON) 
public class JSONProcessingErroMapper implements ExceptionMapper<InvalidFormatException> { 

@Override 
public Response toResponse(InvalidFormatException ex) { 
    return Response.status(400) 
      .entity(new ClientError("[User friendly message]")) 
      .type(MediaType.APPLICATION_JSON) 
      .build(); 
} 

}