2012-01-31 69 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(); 
} 

}