杰克逊:如何使@JsonBackReference和自定义解串器同时工作?

时间:2019-04-30 15:50:20

标签: java json jackson

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

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

我想使用Jackson将这个结构反序列化为DomainUser对象,并且我希望每个用户都对其映射键(它是用户ID)有一个反向引用。Domain容器。这两种方法都行不通,但是如果我尝试一次获得两个反向引用,都将失败。

具有@JsonManagedReference@JsonBackReference的Java类:

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 个答案:

答案 0 :(得分:0)

this answer的帮助下,我找到了解决方案:自定义解串器必须基于默认解串器,该缺省解串器可以正确实现反向引用机制。

这比从正确的基类继承要复杂得多。相反,您需要通过自定义BeanDeserializerModifier来掌握(完全配置的)默认解串器实例,然后将此实例传递给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;
    }
}

最后,您需要删除@JsonDeserialize批注。然后,自定义解串器和@JsonBackReference应该可以工作。