当前位置: 首页 > 知识库问答 >
问题:

Jackson:如何使@JsonBackReference和自定义反序列化器同时工作?

夏侯鹏
2023-03-14

我有一个JSON结构,其中包含字符串到对象的映射:

{
    "domains": {
        "ldap": {
            "users": {
                "fwalther": {
                    "firstName": "Fritz",
                    "lastName": "Walther"
                },
                // ...
            }
        }
    },
    // ...
}       

我希望使用Jackson将此结构反序列化为用户对象,并且我希望每个用户都有一个对其映射键(即用户ID)和容器的反向引用。这两种方法都有效,但如果我试图同时获取两个引用,则会失败。

Java带有@JsonManaged参考@JsonBack参考的类:

public class Domain {
    @JsonManagedReference
    @JsonDeserialize(contentUsing = UserDeserializer.class)
    private Map<String, User> users;

    public Map<String, User> getUsers() {
        return users;
    }
}

public class User {
    @JsonBackReference
    private Domain domain;

    String userId;

    private String firstName;
    private String lastName;

    // ... getters
}

获取映射键的自定义反序列化程序:

public class UserDeserializer extends JsonDeserializer<User> {
    @Override
    public User deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        String key = p.getCurrentName();
        User result = p.readValueAs(User.class);

        result.userId = key;
        return result;
    }
}

这两种机制,即@JsonManagedReference/@JsonBackReference对和@JsonDeserialize与自定义反序列化器,如果我只激活其中一个,它们都可以工作。但是如果我结合这些机制(如上面的代码所示),在解析JSON时会出现以下异常

java.lang.IllegalArgumentException: Cannot handle managed/back reference 'defaultReference': type: value deserializer of type org.example.UserDeserializer does not support them
    at com.fasterxml.jackson.databind.JsonDeserializer.findBackReference(JsonDeserializer.java:366) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase.findBackReference(ContainerDeserializerBase.java:104) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase._resolveManagedReferenceProperty(BeanDeserializerBase.java:786) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:487) ~[jackson-databind-2.9.8.jar:2.9.8]
...

查看引发异常的代码,我发现我需要在自定义反序列化程序中实现findBackReference,但我不知道如何实现,也找不到相关信息。有什么想法吗?

或者有其他方法来获取映射键和包含对象的返回引用?

共有1个答案

施赞
2023-03-14

在这个答案的帮助下,我找到了解决方案:自定义反序列化器需要基于默认的反序列化器,它正确地实现了反向引用机制。

这比仅仅从正确的基类继承要复杂一些。相反,您需要通过自定义的beandSerializerModifier获得(完全配置的)默认反序列化程序实例,然后将此实例传递给DelegatingDeserializer的子类:

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 UserDeserializer(defaultDeserializer);
            } else {
                return defaultDeserializer;
            }
        }
    });
    objectMapper.registerModule(module);

    return objectMapper;
}

然后,自定义反序列化程序需要如下所示:

public class UserDeserializer extends DelegatingDeserializer {

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

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

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

        result.userId = key;
        return result;
    }
}

最后,您需要删除@JsonDeseriize注释。然后,自定义反序列化器和@JsonBack参考应该可以工作。

 类似资料:
  • I'va是一个OID接口,可以由许多具体类型实现: 现在我有一个具有两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义 我想使用jackson以不同的方式序列化/反序列化字段,无论它们是使用抽象接口类型还是具体类型定义的: 注意,被序列化,包括类型信息(多态序列化),而被序列化为文本 为此,我将OID接口注释为: 并为每个具体类型分配了类型id: 最后,对容器

  • 我得到了。了解调用相同的反序列化器。是否有一种方法可以在custome反序列化器中使用或?

  • 如何基于json中指定的类类型,在Jackson中实现从json到Java对象的转换。 Java类型示例:

  • 问题内容: 我正在使用Flickr API 。调用该方法时,默认的JSON结果为: 我想将此响应解析为Java对象: JSON属性应按以下方式映射: 不幸的是,我无法找到一种使用Annotations做到这一点的好方法。到目前为止,我的方法是将JSON字符串读入a 并从中获取值。 但是我认为,这是有史以来最不优雅的方式。有没有简单的方法,可以使用注释还是自定义反序列化器? 这对我来说将是很明显的,

  • 我想通过扩展默认的反序列化器来创建自己的反序列化器,在其后面设置更多的值: 如您所见,我还想将此DTO母类重用于其他DTO。 我没有找到任何这样的例子。我真的是世界上第一个 反序列化的“AsUsual”(p,ctxt)应该是什么 我应该使用什么motherclass?JsonDeserializer/StdDeserializer/UntypedObjectDeserializer 反序列化程序会

  • 我想反序列化表单中的类: 其中文本是加密的,反序列化应该在重建TestFieldEncryptedMessage实例之前取消对值的加密。 我采用的方法非常类似于:https://github.com/codesqueak/jackson-json-crypto 也就是说,我正在构建一个扩展SimpleModule的模块: 如您所见,设置了两个修饰符:EncryptedSerializerModif