Джексон десериализовать на основе имени свойства - PullRequest
0 голосов
/ 22 мая 2018

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

{"foo": "String value"}

и

{"bar": "String value"}

Оба они представляют специализированный тип одного и того же базового объекта.Как я могу использовать Джексона для десериализации их?Информация о типе представлена ​​только самими ключами, а не значением какого-либо ключа (почти во всех примерах значение ключа используется для определения типа: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization)

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

Джексон не предлагает готовое решение для этого, но это не значит, что вам не повезло.


Предполагая, что ваши классы реализуютобщий интерфейс или расширение общего класса, как показано ниже:

public interface Animal {

}
public class Dog implements Animal {

   private String bark;

   // Default constructor, getters and setters
}
public class Cat implements Animal {

   private String meow;

   // Default constructor, getters and setters
}

Вы можете создать собственный десериализатор на основе имени свойства.Это позволяет вам определить уникальное свойство, которое будет использоваться для поиска класса для выполнения десериализации:

public class PropertyBasedDeserializer<T> extends StdDeserializer<T> {

    private Map<String, Class<? extends T>> deserializationClasses;

    public PropertyBasedDeserializer(Class<T> baseClass) {
        super(baseClass);
        deserializationClasses = new HashMap<String, Class<? extends T>>();
    }

    public void register(String property, Class<? extends T> deserializationClass) {
        deserializationClasses.put(property, deserializationClass);
    }

    @Override
    public T deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        ObjectMapper mapper = (ObjectMapper) p.getCodec();
        JsonNode tree = mapper.readTree(p);

        Class<? extends T> deserializationClass = findDeserializationClass(tree);
        if (deserializationClass == null) {
            throw JsonMappingException.from(ctxt, 
               "No registered unique properties found for polymorphic deserialization");
        }

        return mapper.treeToValue(tree, deserializationClass);
    }

    private Class<? extends T> findDeserializationClass(JsonNode tree) {

        Iterator<Entry<String, JsonNode>> fields = tree.fields();
        Class<? extends T> deserializationClass = null;

        while (fields.hasNext()) {
            Entry<String, JsonNode> field = fields.next();
            String property = field.getKey();
            if (deserializationClasses.containsKey(property)) {
                deserializationClass = deserializationClasses.get(property);
                break;  
            }
        }

        return deserializationClass;
    }
}

Затем создать и настроить десериализатор:

UniquePropertyPolymorphicDeserializer<Animal> deserializer = 
        new UniquePropertyPolymorphicDeserializer<>(Animal.class);

deserializer.register("bark", Dog.class); // If "bark" is present, then it's a Dog
deserializer.register("meow", Cat.class); // If "meow" is present, then it's a Cat

Добавьте его в модуль:

SimpleModule module = new SimpleModule("custom-deserializers", Version.unknownVersion());
module.addDeserializer(Animal.class, deserializer);

Зарегистрируйте модуль и выполните десериализацию как обычно:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);

String json = "[{\"bark\":\"bowwow\"}, {\"bark\":\"woofWoof\"}, {\"meow\":\"meeeOwww\"}]";
List<Animal> animals = mapper.readValue(json, new TypeReference<List<Animal>>() { });
0 голосов
/ 22 мая 2018

Вы должны будете сказать Джексону, какой класс вы ожидаете:

Foo readValue = mapper.readValue(json, Foo.class);

Bar readValue = mapper.readValue(json, Bar.class);

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

...