Джексон: Как заставить @JsonBackReference и пользовательский десериализатор работать одновременно? - PullRequest
0 голосов
/ 30 апреля 2019

У меня есть структура JSON, которая содержит сопоставления строк для объекта:

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

Я хочу десериализовать эту структуру для объектов Domain и User, используя Джексона, и я хочу, чтобы у каждого пользователя былообратная ссылка на ключ карты (который является идентификатором пользователя) и на контейнер Domain.Все это работает, но не получается, если я пытаюсь получить обе обратные ссылки одновременно.

Java-классы с @JsonManagedReference и @JsonBackReference:

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 голосов
/ 01 мая 2019

С помощью этого ответа я нашел решение: пользовательский десериализатор должен основываться на десериализаторе по умолчанию, который правильно реализует механизм обратных ссылок.

Это немного сложнее, чем просто наследование от правильного базового класса. Вместо этого вам нужно получить (полностью настроенный) экземпляр десериализатора по умолчанию через пользовательский 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 должны работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...