2012-01-31 7 views
13

Vấn đề của tôi là khá đơn giản: Tôi có lớp đơn giản sau đây:Jackson deserialization lỗi xử lý

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

Và tôi đang cố gắng để xử lý JSON sau:

{ 
    "id": "blah" 
} 

Rõ ràng, có một vấn đề ở đây ("blah" không thể được phân tích cú pháp thành một int)

Trước đây, Jackson ném một cái gì đó như org.codehaus.jackson.map.JsonMappingException: Không thể xây dựng thể hiện của java.lang.Integer từ Chuỗi giá trị 'blah': không phải là giá trị Integer hợp lệ

Tôi đồng ý với điều này, nhưng tôi muốn đăng ký một cái gì đó ở đâu đó cho phép bỏ qua loại lỗi ánh xạ này. Tôi đã thử với một DeserializationProblemHandler đăng ký (xem here) nhưng nó dường như chỉ làm việc trên các thuộc tính không rõ và không phải vấn đề deserialization.

Bạn có bất kỳ đầu mối nào về vấn đề này không?

+0

Tại sao bạn muốn bỏ qua lỗi này? Tôi muốn trả về một mã HTTP của '400' cho mọi khách hàng cố gắng' PUT' cho tôi một đại diện tài nguyên như thế này :) –

+1

Tôi đang sử dụng Jackson với Spring MVC và xác thực bean. Vấn đề là Jackson đang phàn nàn về các vấn đề deserialization, trước khi tôi đạt đến lớp mvc mùa xuân .. vì vậy tôi không thể gửi cho khách hàng của tôi các lỗi một cách nhất quán. –

+0

Ngoài ra tôi (đối với một) sử dụng Jackson khá thường xuyên để làm một bãi chứa có thể đọc được của một đối tượng để đăng nhập. Có thể lưu ý vấn đề serialization và di chuyển trên là rất hữu ích –

Trả lời

9

Tôi đã thành công để giải quyết vấn đề của mình, nhờ vào Tatu from Jackson ML.

Tôi đã phải sử dụng các trình khử giải mã tùy chỉnh không chặn cho mọi loại nguyên thủy được xử lý ở Jackson. Something như nhà máy này:

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; 
    } 
} 

Sau đó gọi nó như cách này (vượt qua như deserializers chỉ những bạn muốn trở thành phi chặn):

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

Bạn có thể muốn để cho điều khiển của bạn xử lý vấn đề bằng cách thêm một phương pháp để xử lý ngoại lệ cụ thể này

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

Tất nhiên, dòng

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

có thể ném ngoại lệ truyền lớp cho một số trường hợp nhưng tôi chưa gặp phải chúng.

+1

Bạn đang phải, đây có thể là một giải pháp hợp lệ. Tuy nhiên, nó có một số hạn chế: - Bạn sẽ có lỗi báo cáo từng cái một (bạn sẽ cần 3 yêu cầu để xác định bạn có 3 trường được định dạng sai). Đặt giá trị rỗng khi trường bị định dạng sai cho phép tôi đặt chú thích \ @NotNull trên trường để có danh sách lỗi đầy đủ trong 1 yêu cầu - Điều này cho phép xử lý từng trường hợp trên từng trường.Nếu một số trường bị định dạng sai * và * điều này không quan trọng, tôi không đặt lỗi \ @NotNull trên các trường này => sẽ được lọc –

+0

Có, bạn nói đúng, cách tiếp cận của bạn tốt hơn. Tôi quan tâm đến cách bạn làm cho nó hoạt động với giá trị nguyên thủy, vì tôi muốn mô hình miền của mình có ** tuổi riêng **, thay vì ** tuổi Integer riêng **. Tôi đã giải quyết được điều này bằng cách sử dụng ** new StdDeserializer.IntegerDeserializer (int.class, 0), ** và thay thế ** return null ** từ phương thức deserialize với ** return delegate.getNullValue(); **. Nếu bạn nghĩ rằng đây là giải pháp đúng, vui lòng cập nhật mã của bạn để bao gồm điều này, nếu bạn có cách tiếp cận khác, vui lòng chia sẻ. –

+0

Tôi tự hỏi liệu có nên xem xét các loại thô trong trường hợp đó hay không. Bởi vì nó có vẻ kỳ lạ với tôi để sử dụng một số giá trị để đại diện cho một giá trị "bất hợp pháp" (trong trường hợp của bạn, giá trị có vẻ là 0). Tôi muốn sử dụng một giá trị ít được sử dụng như Integer.MAX_VALUE, nhưng ngay cả trong trường hợp đó, tôi thấy hành vi này một chút quá tùy ý (đây là một POV personnal) –

0

Tạo một Mapper đơn giản:

@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(); 
} 

}