130

Jackson のカスタム デシリアライザーに問題があります。デフォルトのシリアライザーにアクセスして、デシリアライズするオブジェクトを設定したいと考えています。ポピュレーションの後、いくつかのカスタム処理を行いますが、最初にデフォルトの Jackson の動作でオブジェクトをデシリアライズしたいと考えています。

これは私が現時点で持っているコードです。

public class UserEventDeserializer extends StdDeserializer<User> {

  private static final long serialVersionUID = 7923585097068641765L;

  public UserEventDeserializer() {
    super(User.class);
  }

  @Override
  @Transactional
  public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {

    ObjectCodec oc = jp.getCodec();
    JsonNode node = oc.readTree(jp);
    User deserializedUser = null;
    deserializedUser = super.deserialize(jp, ctxt, new User()); 
    // The previous line generates an exception java.lang.UnsupportedOperationException
    // Because there is no implementation of the deserializer.
    // I want a way to access the default spring deserializer for my User class.
    // How can I do that?

    //Special logic

    return deserializedUser;
  }

}

必要なのは、特別なロジックを開始する前に POJO を事前設定できるように、デフォルトのデシリアライザーを初期化する方法です。

カスタムデシリアライザー内からデシリアライズを呼び出す場合シリアライザークラスをどのように構築しても、現在のコンテキストからメソッドが呼び出されるようです。私のPOJOの注釈のため。これにより、明らかな理由でスタック オーバーフロー例外が発生します。

を初期化しようとしましたBeanDeserializerが、プロセスが非常に複雑で、適切な方法を見つけることができませんでした。AnnotationIntrospectorまた、 の注釈を無視するのに役立つかもしれないと考えて、 を無駄にオーバーロードしようとしましたDeserializerContext。最後にJsonDeserializerBuilders、Spring からアプリケーション コンテキストを取得するために魔法のようなことをする必要がありましたが、これを使用してある程度成功した可能性があるようです。JsonDeserializerたとえば、注釈を読み取らずに逆シリアル化コンテキストを構築する方法など、よりクリーンなソリューションにつながる可能性があることを感謝します。

4

11 に答える 11

105

StaxMan が既に示唆しているように、これを行うには、a を作成し、BeanDeserializerModifierそれを介して登録しSimpleModuleます。次の例は機能するはずです。

public class UserEventDeserializer extends StdDeserializer<User> implements ResolvableDeserializer
{
  private static final long serialVersionUID = 7923585097068641765L;

  private final JsonDeserializer<?> defaultDeserializer;

  public UserEventDeserializer(JsonDeserializer<?> defaultDeserializer)
  {
    super(User.class);
    this.defaultDeserializer = defaultDeserializer;
  }

  @Override public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException
  {
    User deserializedUser = (User) defaultDeserializer.deserialize(jp, ctxt);

    // Special logic

    return deserializedUser;
  }

  // for some reason you have to implement ResolvableDeserializer when modifying BeanDeserializer
  // otherwise deserializing throws JsonMappingException??
  @Override public void resolve(DeserializationContext ctxt) throws JsonMappingException
  {
    ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
  }


  public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException
  {
    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier()
    {
      @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
      {
        if (beanDesc.getBeanClass() == User.class)
          return new UserEventDeserializer(deserializer);
        return deserializer;
      }
    });


    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(module);
    User user = mapper.readValue(new File("test.json"), User.class);
  }
}
于 2013-08-23T14:56:45.213 に答える
9

これを行うにはいくつかの方法がありますが、正しく行うにはもう少し作業が必要です。デフォルトのデシリアライザーが必要とする情報はクラス定義から構築されるため、基本的にサブクラス化は使用できません。

したがって、最も可能性が高いのは、を構築し、インターフェースBeanDeserializerModifierを介して登録することです(を使用)。を定義/オーバーライドする必要があり、独自のロジック (型が一致する場合) を追加する特定のケースでは、独自のデシリアライザーを構築し、指定されたデフォルトのデシリアライザーを渡します。そして、メソッドでは、呼び出しを委任して、結果のオブジェクトを取得できます。ModuleSimpleModulemodifyDeserializerdeserialize()

または、実際にオブジェクトを作成して設定する必要がある場合は、そのようにして、deserialize()3 番目の引数を取る のオーバーロード バージョンを呼び出すことができます。逆シリアル化するオブジェクト。

機能する可能性がある別の方法 (100% 確実ではありません) は、Converterオブジェクト ( @JsonDeserialize(converter=MyConverter.class)) を指定することです。これは新しい Jackson 2.2 機能です。あなたの場合、 Converter は実際には型を変換しませんが、オブジェクトを単純に変更します:しかし、デフォルトのデシリアライザーが最初に呼び出され、次にあなたのConverter.

于 2013-08-21T19:23:52.093 に答える
0

私にとってより簡単な解決策は、別の Bean を追加し、それを使用してオブジェクトを逆シリアル化することでした ( https://stackoverflow.com/users/1032167/varrenObjectMapperコメントに感謝します) - 私の場合、その ID に逆シリアル化することに興味がありました(int) またはオブジェクト全体https://stackoverflow.com/a/46618193/986160

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.springframework.context.annotation.Bean;

import java.io.IOException;

public class IdWrapperDeserializer<T> extends StdDeserializer<T> {

    private Class<T> clazz;

    public IdWrapperDeserializer(Class<T> clazz) {
        super(clazz);
        this.clazz = clazz;
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        return mapper;
    }

    @Override
    public T deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
        String json = jp.readValueAsTree().toString();
          // do your custom deserialization here using json
          // and decide when to use default deserialization using local objectMapper:
          T obj = objectMapper().readValue(json, clazz);

          return obj;
     }
}

カスタムデシリアライザーを通過する必要があるエンティティごとに、私の場合 (例: の場合) ObjectMapper、Spring Boot アプリのグローバル Bean で構成する必要があります。Category

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
            mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
            mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    SimpleModule testModule = new SimpleModule("MyModule")
            .addDeserializer(Category.class, new IdWrapperDeserializer(Category.class))

    mapper.registerModule(testModule);

    return mapper;
}
于 2017-10-07T08:38:04.487 に答える