web-dev-qa-db-ja.com

ジャクソンの逆シリアル化エラー処理

私の問題はかなり単純です。次の単純なクラスがあります。

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

そして、私は次のJSONを処理しようとしています:

{
  "id": "blah"
}

明らかに、ここに問題があります(「何とか」はintに解析できません)

以前は、Jacksonはorg.codehaus.jackson.map.JsonMappingExceptionのようなものをスローします:文字列値 'blah'からJava.lang.Integerのインスタンスを構築できません:有効な整数値ではありません

私はこれに同意しますが、このタイプのマッピングエラーを無視できる場所に何かを登録したいと思います。 DeserializationProblemHandlerを登録して試してみましたが( こちら を参照)、不明なプロパティでのみ機能し、逆シリアル化の問題では機能しないようです。

この問題について何か手がかりはありますか?

17
Frédéric

Jackson MLのTat のおかげで、問題を解決することができました。

ジャクソンで処理されるすべてのプリミティブ型に対して、カスタムのノンブロッキングデシリアライザを使用する必要がありました。この工場のようなもの:

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();
16
Frédéric

この特定の例外を処理するメソッドを追加して、コントローラーに問題を処理させることができます

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

場合によってはクラスキャスト例外をスローすることがありますが、まだ発生していません。

8
E L

単純なマッパーを作成します。

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

}

1