如何从Jackson中的自定义反序列化器调用默认反序列化器

Pab*_*mer 92 java spring hibernate 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;
  }

}
Run Code Online (Sandbox Code Playgroud)

我需要的是一种初始化默认反序列化器的方法,这样我就可以在开始我的特殊逻辑之前预先填充我的POJO.

从自定义反序列化器中调用反序列化时,无论我如何构造序列化器类,都会从当前上下文调用该方法.因为我的POJO中有注释.这会导致Stack Overflow异常,原因很明显.我已经尝试初始化beandeserializer,但是这个过程非常复杂,我还没有找到正确的方法来完成它.我也尝试重载注释内部跟踪器无济于事,认为它可以帮助我忽略DeserializerContext中的注释.最后它接缝我可能使用JsonDeserializerBuilders取得了一些成功,虽然这要求我做一些神奇的东西来从春天获得应用程序上下文.我将不胜感激,这可能会让我找到一个更清晰的解决方案,例如如何在不读取JsonDeserializer注释的情况下构建反序列化上下文.

小智 86

正如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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法做同样的但是使用`JsonSerializer`?我有几个序列化程序,但他们有共同的代码,所以我想要生成它.我尝试直接调用序列化程序,但结果未在JSON结果中解包(序列化程序的每次调用都会创建一个新对象) (5认同)
  • 如果您使用“DelegatingDeserializer”作为基类,则不需要实现“resolve” - 请参阅[此答案](/sf/answers/3915682151/) (2认同)

Der*_*ran 9

DeserializationContext有一个readValue()你可以使用的方法.这应该适用于默认的反序列化器和您拥有的任何自定义反序列化器.

只需确保调用要读取traverse()JsonNode级别以检索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;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 您的解决方案是最优雅的解决方案。您将 BarBean.class 的序列化调度委托给 Jackson。这很好,您可以使反序列化器变得更小、可重用且可测试。我相信您应该调用 JsonNode.traverse(codec) 来传递现有的反序列化器编解码器,而不是 JsonNode.traverse() 。 (4认同)
  • 此解决方案运行良好,但是如果您反序列化值类(例如 Date.class),则可能需要调用 nextToken() (3认同)

Sta*_*Man 8

有几种方法可以做到这一点,但要做到这一点涉及更多的工作.基本上你不能使用子类,因为信息默认的反序列化需要是从类定义构建的.

所以你最有可能使用的是构造一个BeanDeserializerModifier,通过Module接口(使用SimpleModule)注册.您需要定义/覆盖modifyDeserializer,对于您想要添加自己的逻辑(类型匹配)的特定情况,构造您自己的反序列化器,传递给定的默认反序列化器.然后在deserialize()方法中你可以委托调用,获取结果Object.

或者,如果您必须实际创建并填充对象,则可以执行此操作并调用重载版本的deserialize()第三个参数; 反序列化的对象.

可能有效(但不是100%肯定)的另一种方法是指定Converterobject(@JsonDeserialize(converter=MyConverter.class)).这是Jackson 2.2的新功能.在你的情况下,转换器实际上不会转换类型,但是简化修改对象:但是我不知道是否会让你完全按照自己的意愿行事,因为默认的反序列化器将首先被调用,然后才会被调用Converter.


Gil*_*ili 8

我在/sf/answers/3634930421/找到了一个答案,它比接受的答案更具可读性。

public User deserialize(JsonParser jp, DeserializationContext ctxt)
    throws IOException, JsonProcessingException {
        JsonNode tree = jp.readTree(jp);

        // To call the default deserializer, simply invoke:
        User user = tree.get("user").traverse(jp.getCodec()).readValueAs(User.class);
        return user;
      }
Run Code Online (Sandbox Code Playgroud)

确实没有比这更容易的了。

  • 我不明白这是如何工作的,这会导致“StackOverflowError”,因为杰克逊将再次对“User”使用相同的序列化器...... (22认同)

obe*_*ies 6

如果您尝试从头开始创建自定义解串器,您注定会失败。

相反,您需要通过 custom 获取(完全配置的)默认反序列化器实例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;
}
Run Code Online (Sandbox Code Playgroud)

注意:此模块注册取代了@JsonDeserialize注解,即User类或User字段不应再用此注解进行注解。

然后,自定义反序列化器应该基于 a ,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;
    }
}
Run Code Online (Sandbox Code Playgroud)


hop*_*per 5

按照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;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

如果可以声明额外的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;
  }

}
Run Code Online (Sandbox Code Playgroud)

  • 是的。唯一对我有用的方法。由于对反序列化器的递归调用,我收到了 StackOverflowErrors。 (2认同)