簡體   English   中英

如何從 Jackson 中的自定義解串器調用默認解串器

[英]How do I call the default deserializer from a custom deserializer in Jackson

我在 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注釋的情況下構建反序列化上下文。

正如 StaxMan 已經建議您可以通過編寫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);
  }
}

DeserializationContext有一個你可以使用的readValue()方法。 這應該適用於默認解串器和您擁有的任何自定義解串器。

請務必在要讀取的JsonNode級別上調用traverse()以檢索JsonParser以傳遞給readValue()

public class FooDeserializer extends StdDeserializer<FooBean> {

    private static final long serialVersionUID = 1L;

    public FooDeserializer() {
        this(null);
    }

    public FooDeserializer(Class<FooBean> t) {
        super(t);
    }

    @Override
    public FooBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        FooBean foo = new FooBean();
        foo.setBar(ctxt.readValue(node.get("bar").traverse(), BarBean.class));
        return foo;
    }

}

我在ans找到了一個比公認的答案更具可讀性的答案。

    public User deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
            User user = jp.readValueAs(User.class);
             // some code
             return user;
          }

真的沒有比這更容易的了。

有幾種方法可以做到這一點,但要正確地做到這一點需要做更多的工作。 基本上你不能使用子類,因為默認反序列化器需要的信息是從類定義中構建的。

所以你最有可能使用的是構造一個BeanDeserializerModifier ,通過Module接口注冊它(使用SimpleModule )。 您需要定義/覆蓋modifyDeserializer ,並且對於您想要添加自己的邏輯(類型匹配)的特定情況,構造您自己的反序列化器,傳遞給定的默認反序列化器。 然后在deserialize()方法中,您可以委托調用,獲取結果對象。

或者,如果您必須實際創建和填充對象,您可以這樣做並調用deserialize()第三個參數的deserialize()重載版本; 要反序列化的對象。

另一種可能有效(但不是 100% 確定)的方法是指定Converter對象( @JsonDeserialize(converter=MyConverter.class) )。 這是 Jackson 2.2 的新功能。 在您的情況下, Converter 實際上不會轉換類型,而是簡化修改對象:但我不知道這是否會讓您完全按照自己的意願行事,因為將首先調用默認解串器,然后才調用您的Converter

如果您可以聲明額外的 User 類,那么您可以僅使用注釋來實現它

// your class
@JsonDeserialize(using = UserEventDeserializer.class)
public class User {
...
}

// extra user class
// reset deserializer attribute to default
@JsonDeserialize
public class UserPOJO extends User {
}

public class UserEventDeserializer extends StdDeserializer<User> {

  ...
  @Override
  public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
    // specify UserPOJO.class to invoke default deserializer
    User deserializedUser = jp.ReadValueAs(UserPOJO.class);
    return deserializedUser;

    // or if you need to walk the JSON tree

    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    JsonNode node = oc.readTree(jp);
    // specify UserPOJO.class to invoke default deserializer
    User deserializedUser = mapper.treeToValue(node, UserPOJO.class);

    return deserializedUser;
  }

}

按照Tomáš Záluský 的建議,在不希望使用BeanDeserializerModifier情況下,您可以使用BeanDeserializerFactory自己構建默認反序列化器,盡管需要一些額外的設置。 在上下文中,此解決方案如下所示:

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

    ObjectCodec oc = jp.getCodec();
    JsonNode node = oc.readTree(jp);
    User deserializedUser = null;

    DeserializationConfig config = ctxt.getConfig();
    JavaType type = TypeFactory.defaultInstance().constructType(User.class);
    JsonDeserializer<Object> defaultDeserializer = BeanDeserializerFactory.instance.buildBeanDeserializer(ctxt, type, config.introspect(type));

    if (defaultDeserializer instanceof ResolvableDeserializer) {
        ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
    }

    JsonParser treeParser = oc.treeAsTokens(node);
    config.initialize(treeParser);

    if (treeParser.getCurrentToken() == null) {
        treeParser.nextToken();
    }

    deserializedUser = (User) defaultDeserializer.deserialize(treeParser, context);

    return deserializedUser;
}

我不同意使用BeanSerializerModifier因為它強制在中央ObjectMapper而不是在自定義反序列化器本身中聲明一些行為更改,實際上它是使用JsonSerialize注釋實體類的並行解決方案。 如果你有類似的感覺,你可能會喜歡我的回答: https : //stackoverflow.com/a/43213463/653539

如果您嘗試從頭開始創建自定義反序列化器,您一定會失敗。

相反,您需要通過自定義BeanDeserializerModifier獲取(完全配置的)默認反序列化器實例,然后將此實例傳遞給您的自定義反序列化器類:

public ObjectMapper getMapperWithCustomDeserializer() {
    ObjectMapper objectMapper = new ObjectMapper();

    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                    BeanDescription beanDesc, JsonDeserializer<?> defaultDeserializer) 
            if (beanDesc.getBeanClass() == User.class) {
                return new UserEventDeserializer(defaultDeserializer);
            } else {
                return defaultDeserializer;
            }
        }
    });
    objectMapper.registerModule(module);

    return objectMapper;
}

注意:此模塊注冊替換了@JsonDeserialize注解,即User類或User字段不應再使用此注解進行注解。

然后自定義反序列化器應該基於DelegatingDeserializer以便所有方法都委托,除非您提供顯式實現:

public class UserEventDeserializer extends DelegatingDeserializer {

    public UserEventDeserializer(JsonDeserializer<?> delegate) {
        super(delegate);
    }

    @Override
    protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegate) {
        return new UserEventDeserializer(newDelegate);
    }

    @Override
    public User deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException {
        User result = (User) super.deserialize(p, ctxt);

        // add special logic here

        return result;
    }
}

這是一個使用 ObjectMapper 的 oneliner

public MyObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    MyObject object = new ObjectMapper().readValue(p, MyObject.class);
    // do whatever you want 
    return object;
}

並且請:真的沒有必要使用任何字符串值或其他東西。 JsonParser 提供了所有需要的信息,因此請使用它。

使用BeanDeserializerModifier效果很好,但如果您需要使用JsonDeserialize有一種方法可以使用AnnotationIntrospector來做到這一點,如下所示:

ObjectMapper originalMapper = new ObjectMapper();
ObjectMapper copy = originalMapper.copy();//to keep original configuration
copy.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {

            @Override
            public Object findDeserializer(Annotated a) {
                Object deserializer = super.findDeserializer(a);
                if (deserializer == null) {
                    return null;
                }
                if (deserializer.equals(MyDeserializer.class)) {
                    return null;
                }
                return deserializer;
            }
});

現在復制的映射器現在將忽略您的自定義反序列化器 (MyDeserializer.class) 並使用默認實現。 您可以在自定義反deserialize方法中使用它,通過將復制的映射器設為靜態或在使用 Spring 時連接它來避免遞歸。

對我來說,一個更簡單的解決方案是添加另一個ObjectMapper bean 並使用它來反序列化對象(感謝https://stackoverflow.com/users/1032167/varren評論) - 在我的情況下,我有興趣要么反序列化到它的id(整數)或整個對象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;
     }
}

對於需要通過自定義反序列化器的每個實體,我們需要在 Spring Boot App 的全局ObjectMapper 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM