web-dev-qa-db-ja.com

カスタムデシリアライザー内でjacksonObjectMapperをどのように使用しますか?

私はカスタムジャクソンデシリアライザーを書こうとしています。 1つのフィールドを「見て」、クラスへの自動逆シリアル化を実行したいのですが、以下の例を参照してください。

_import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.mypackage.MyInterface;
import com.mypackage.MyFailure;
import com.mypackage.MySuccess;

import Java.io.IOException;

public class MyDeserializer extends JsonDeserializer<MyInterface> {

    @Override
    public MyInterface deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        ObjectCodec codec = jp.getCodec();
        JsonNode node = codec.readTree(jp);
        if (node.has("custom_field")) {
            return codec.treeToValue(node, MyFailure.class);
        } else {
            return codec.treeToValue(node, MySuccess.class);
        }
    }
}
_

ポジョス:

_public class MyFailure implements MyInterface {}

public class MySuccess implements MyInterface {}

@JsonDeserialize(using = MyDeserializer.class)
public interface MyInterface {}
_

そして、私はStackOverflowErrorを手に入れました。 _codec.treeToValue_が同じデシリアライザーを呼び出すことを理解してください。カスタム脱セラライザー内で_codec.treeToValue_またはObjectMapper.readValue(String,Class<T>)を使用する方法はありますか?

16
Cherry

差し迫った問題は、MyInterfaceの実装とMyInterface自体のために@JsonDeserialize(using=...)が取得されていること、つまり無限のループであるようです。

これを修正して、各実装の設定を上書きすることができます。

@JsonDeserialize(using=JsonDeserializer.None.class)
public static class MySuccess implements MyInterface {
}

または、アノテーションの代わりにモジュールを使用して逆シリアル化を構成する(およびMyInterfaceからアノテーションを削除する):

mapper.registerModule(new SimpleModule() {{
    addDeserializer(MyInterface.class, new MyDeserializer());
}});

ちなみに、JsonNodeに基づく逆シリアル化を実装するためにStdNodeBasedDeserializerを拡張することも検討してください。例えば:

@Override
public MyInterface convert(JsonNode root, DeserializationContext ctxt) throws IOException {
    Java.lang.reflect.Type targetType;
    if (root.has("custom_field")) {
        targetType = MyFailure.class;
    } else {
        targetType = MySuccess.class;
    }
    JavaType jacksonType = ctxt.getTypeFactory().constructType(targetType);
    JsonDeserializer<?> deserializer = ctxt.findRootValueDeserializer(jacksonType);
    JsonParser nodeParser = root.traverse(ctxt.getParser().getCodec());
    nodeParser.nextToken();
    return (MyInterface) deserializer.deserialize(nodeParser, ctxt);
}

このカスタムデシリアライザーには、特にデシリアライズのコンテキストの追跡などに関して、多くの改善が加えられていますが、これにより、必要な機能が提供されるはずです。

8
araqnid

これは私にとってトリックでした:

ctxt.readValue(node, MyFailure.class)
0
Julio

カスタムデシリアライザー内で独自のObjectMapperを使用するには、 Jackson Mix-in AnnotationstheDefaultJsonDeserializerinterfacePOJOクラスからカスタムデシリアライザーを動的に削除し、StackOverflowErrorobjectMapper.readValue(JsonParser, Class<T>)の結果としてスローされます。

public class MyDeserializer extends JsonDeserializer<MyInterface> {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.addMixIn(MySuccess.class, DefaultJsonDeserializer.class);
        objectMapper.addMixIn(MyFailure.class, DefaultJsonDeserializer.class);
    }

    @Override
    public MyInterface deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        if (jp.getCodec().<JsonNode>readTree(jp).has("custom_field")) {
            return objectMapper.readValue(jp, MyFailure.class);
        } else {
            return objectMapper.readValue(jp, MySuccess.class);
        }           
    }

    @JsonDeserialize
    private interface DefaultJsonDeserializer {
        // Reset default json deserializer
    }

}
0
lcnicolau