Привязка JSON к вложенным объектам домена Grails - PullRequest
39 голосов
/ 23 сентября 2011

Я разрабатываю интерфейс RESTful, который используется для предоставления данных JSON для приложения JavaScript.

На стороне сервера я использую Grails 1.3.7 и использую доменные объекты GORM для постоянного хранения. Я реализовал пользовательский JSON Marshaller для поддержки маршалинга вложенных доменных объектов

Вот примеры объектов домена:

class SampleDomain {
    static mapping = { nest2 cascade: 'all' }
    String someString
    SampleDomainNested nest2
}

и

class SampleDomainNested {
    String someField
}

Ресурс SampleDomain публикуется под URL / rs / sample / so / rs / sample / 1, указывающим на объект SampleDomain с идентификатором 1

Когда я рендерил ресурс, используя мой собственный маршаллер json (GET on / rs / sample / 1), я получаю следующие данные:

{
    "someString" : "somevalue1",
    "nest2" : {
        "someField" : "someothervalue"
    }
}

это именно то, что я хочу.

Теперь возникает проблема: я пытаюсь отправить те же данные на ресурс / rs / sample / 1 через PUT.

Чтобы привязать данные json к объекту домена, контроллер, обрабатывающий запрос, вызывает def domain = SampleDomain.get(id) и domain.properties = data, где данные - это неупорядоченный объект.

Привязка для поля "someString" работает просто отлично, но вложенный объект не заполняется с использованием вложенных данных, поэтому я получаю сообщение об ошибке, что свойство "nest2" имеет значение null, что недопустимо.

Я уже пытался реализовать пользовательский PropertyEditorSupport, а также StructuredPropertyEditor и зарегистрировать редактор для класса.

Странно, редактор вызывается только тогда, когда я предоставляю не вложенные значения. Поэтому, когда я отправляю следующее на сервер через PUT (что не имеет никакого смысла;))

{
    "someString" : "somevalue1",
    "nest2" : "test"
}

как минимум вызывается редактор свойств.

Я посмотрел код GrailsDataBinder. Я обнаружил, что установка свойств ассоциации, по-видимому, работает путем указания пути ассоциации вместо предоставления карты, поэтому также работает следующее:

{
    "someString" : "somevalue1",
    "nest2.somefield" : "someothervalue"
}

но это не помогает мне, поскольку я не хочу реализовывать пользовательский сериализатор объектов JavaScript в JSON.

Можно ли использовать привязку данных Grails с использованием вложенных карт? Или я действительно хочу реализовать это вручную для каждого класса домена?

Большое спасибо,

Martin

Ответы [ 3 ]

7 голосов
/ 31 мая 2012

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

Поскольку у меня было еще несколько требований для реализации, таких как безопасность и т. Д., Я реализовал сервисный уровень, который скрывает доменные объекты от контроллеров. Я ввел «динамический слой DTO», который переводит доменные объекты в Groovy Maps, которые можно легко сериализовать с помощью стандартных сериализаторов, и который выполняет обновления вручную. Все полуавтоматические решения, основанные на метапрограммировании / шаблонах команд / ..., которые я пытался реализовать, в какой-то момент потерпели неудачу, в основном приводя к странным ошибкам GORM или большому количеству кода конфигурации (и большому разочарованию). Методы обновления и сериализации для DTO довольно просты и могут быть реализованы очень быстро. Он также не содержит большого количества дублирующегося кода, поскольку вы все равно должны указать, как ваши доменные объекты сериализуются, если вы не хотите публиковать свою внутреннюю структуру объектов домена. Возможно, это не самое элегантное решение, но это было единственное решение, которое действительно сработало для меня. Это также позволяет мне выполнять пакетные обновления, поскольку логика обновления больше не связана с http-запросами.

Однако я должен сказать, что я не думаю, что grails - это подходящий технологический стек, наилучшим образом подходящий для такого рода приложений, поскольку он делает ваше приложение очень тяжелым и несгибаемым. По моему опыту, как только вы начинаете делать вещи, которые не поддерживаются фреймворком по умолчанию, он начинает запутываться. Кроме того, мне не нравится тот факт, что слой «хранилище» в Grails по существу существует только как часть объектов домена, что создает много проблем и приводит к тому, что несколько «прокси-служб» эмулируют слой хранилища. Если вы начинаете создавать приложение с использованием интерфейса отдыха json, я бы предложил либо использовать очень легковесную технологию, такую ​​как node.js, либо, если вы хотите / должны придерживаться стека на основе Java, использовать стандартную среду Spring + spring mvc + spring data с красивым и чистым слоем dto (это то, к чему я перешел, и оно работает как шарм). Вам не нужно писать много стандартного кода, и вы полностью контролируете то, что происходит на самом деле. Кроме того, вы получаете строгую типизацию, которая увеличивает производительность разработчика, а также удобство сопровождения и которая узаконивает дополнительные LOC. И, конечно же, строгая типизация означает сильную работу!

Я начал писать запись в блоге, описывающую архитектуру, которую я придумал (с примером проекта, конечно), однако у меня сейчас не так много времени, чтобы закончить его. Когда это будет сделано, я буду ссылаться на него здесь для справки.

Надеюсь, что это может послужить вдохновением для людей, испытывающих подобные проблемы.

Ура!

1 голос
/ 12 января 2012

Не знаю, почему вы написали свой собственный json marshaller, с xstream вокруг.

См. http://x -stream.github.io / json-tutorial.html

Мы были очень довольны xstream для наших внутренних (основанных на grails) сервисов, и таким образом вы можете визуализировать маршалл в xml или json или, если хотите, переопределить маршаллинг по умолчанию для конкретного объекта.

Кажется, что Jettison создает более компактный, менее читаемый человеком JSON, и вы можете столкнуться с некоторыми библиотеками столкновений, но по умолчанию внутренний рендерер потока json приличный.

Если вы собираетесь опубликовать службу для общего доступа, вам понадобится время, чтобы вернуть соответствующие ответы протокола HTTP для ошибок и т. Д. ($ .02)

1 голос
/ 10 января 2012

Требуется указать имя класса:

{ class:"SampleDomain", someString: "abc", 
nest2: { class: "SampleDomainNested", someField:"def" }
} 

Я знаю, он требует другого ввода, чем вывод, который он производит.

Как я уже упоминал в комментарии ранее, вы можетелучше использовать библиотеку gson.

...