Полиморфизм или наследование в JSON с помощью Java и Ruby

Для контекста мы храним большую часть наших данных в виде строк JSON. Это очень хорошо работает с Hadoop на бэкэнде и легко реализуется в Ruby во фронтэнде. Мои типы данных соответствуют естественному шаблону наследования.

Для простоты предположим, что у меня есть класс Pet и процесс FeedPet, который кормит питомца. У меня также есть процесс WalkDog, который применим только к собаке, которая является своего рода домашним животным. Мои данные организованы таким образом, что мне не нужно беспокоиться о том, чтобы пытаться выгуливать домашнее животное, которое не является собакой.

Я бы хотел, чтобы Pet и Dog расширяли Pet, а Dog имел дополнительный метод getLeash (), но я не могу понять, как сопоставить это с JSON.

Вместо этого у меня есть класс Pet с хэш-картой данных вида, поэтому процесс WalkDog будет вызывать pet.getSpeciesData ("leash") вместо dog.getLeash ().

Я могу создать Dog extends Pet и сериализовать его в JSON с помощью библиотеки Джексона. У объекта JSON будет поле привязи. Но предположим, что я хочу накормить всех домашних животных. У всех питомцев есть метод getFood (). Итак, процесс FeedPet десериализует объекты в Pet. Но при этом теряется поводковое поле.

Процесс WalkDog может это сделать, потому что он знает, что все его входные данные будут собаками, поэтому он может читать его как Dog и записывать обратно как Dog.

Есть ли способ сериализовать java-объекты в JSON, чтобы я мог сохранить их тип? Я думаю о чем-то вроде наследования одной таблицы Rails, но это должно быть что-то, что понимают библиотеки JSON.


person Kevin Peterson    schedule 07.04.2009    source источник


Ответы (2)


Чтобы заставить его работать, вы должны как встроить некоторую информацию о типе объекта в данные (где это полезно только для десериализации), так и обычно использовать определение внешней схемы, которое в противном случае не понадобилось бы (например, XML-схема для xml; поскольку это в основном система общих типов) . Это имеет те же проблемы, что и ORM: Hibernate должен использовать уродливые обходные пути (n + 1-сторонние соединения, «супертаблицы» или поля дискриминатора).

Или иначе: сопоставление / привязка данных - это не совсем то же самое, что сериализация / десериализация объекта (последняя пытается сохранить больше идентичности объекта).

Здесь я предполагаю, что вы хотите в основном что-то вроде:

Pet pet = mapper.readValue(jsonString, Pet.class);
// (and possibly get an exception if Pet is an abstract class...)
Leash l = ((Dog) pet).getLeash();

Если это не так, вы можете просто привязать к Dog

Dog dog = mapper.readValue(jsonString, Dog.class);

Так или иначе: в проекте Джексона есть запрос функции для выполнения именно этого, что позволяет вам делать что (я думаю) ты хочешь.

Однако это решение в основном будет работать для Java, поскольку нет стандартного способа передачи информации о типе объекта в JSON. В XML это можно сделать с помощью атрибута xsi: type, определенного в XML-схеме; который определяет тип схемы, который затем сопоставляется с классом (да, довольно сложный способ, но он работает).

ОБНОВЛЕНИЕ: в Jackson 1.5 добавлена ​​поддержка для этого (т.е. реализована функция JACKSON-91 request), поэтому его можно использовать для генерации идентификаторов типов, чтобы обеспечить правильную обработку полиморфных типов. Он также должен работать с системами, отличными от Java, учитывая, что вы можете полностью настроить детали того, как должна быть включена информация о типе; и НЕ ограничивается использованием имен классов Java.

person StaxMan    schedule 08.04.2009
comment
Думаю, вы ответили на суть вопроса, стандартного способа представления этого в JSON не существует. JACKSON-91 даст мне именно то, что я хочу, поскольку в моем случае я читаю данные только в Ruby. - person Kevin Peterson; 09.04.2009
comment
Ах хорошо. На самом деле, 91 понадобится только в том случае, если вам нужно будет читать его на Java. Для записи информации о классе могут быть более простые способы (хотя 91 также, очевидно, охватил бы это). Один из способов - сделать Object.getClass () сериализуемым (SerializationConfig.Feature.OUTPUT_CLASS_NAME?). - person StaxMan; 09.04.2009

У меня не было такого большого опыта использования JSON, кроме фикстур в django, но в этом случае у меня есть просто «модельный» ключ со связанным значением.

Затем программа, которая интерпретирует объекты, определяет их тип и иерархию наследования.

Я пытаюсь сказать, что не имеет значения, какие методы доступны, поскольку их не нужно сериализовать. Только имя объекта и атрибуты.

-- Обновить

После повторного прочтения вашего вопроса кажется, что ваш процесс десериализуется в родительский класс, а не в фактический класс. Поскольку ваш класс dog наследуется от pet, вы просто хотите убедиться, что ваш десериализатор создает объекты самого специализированного класса.

Я не знаю о библиотеке Джексона, но если вы можете установить для нее тип поля модели, то это может быть подходящим вариантом.

person Matthew Schinckel    schedule 08.04.2009