(Опубликован ответ от имени автора вопроса) .
Шаг 1) если вы создаете новый контроллер (я использовал grails create-controller <controller>
, а затем редактируете сгенерированный контроллер для расширения от RestfulController<domain type>
(но не переопределяете никакие методы). Затем настройте сопоставление URL для вашего контроллера, в моем примере это было
"/api/org"(resources:'OrgRoleInstance')
, для которого у меня есть соответствующий класс домена 'OrgRoleInstance' в моей модели.
Действие create-controller также генерирует пустой пакет представления 'views / orgRoleInstance' (файлы gson не создаются)
а) если вы сейчас запускаете приложение и открываете URL
http://localhost:8080/api/org
тогда вы все равно получите ответ (как этот)!
[{"id":1,"sites":[],"domains":[],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Service_Provider"},"name":"Vodafone","mags":[{"id":2}]},{"id":2,"sites":[],"domains":[],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Maintainer"},"name":"Cisco","mags":[{"id":1}]},{"id":3,"sites":[],"domains":[],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Supplier"},"name":"Cisco","mags":[]},{"id":4,"sites":[{"id":1},{"id":2}],"domains":[{"id":1}],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Customer"},"name":"Acme","mags":[]}]
Ipso facto существует некоторая «логика фреймворка по умолчанию», которая пытается отобразить реализацию по умолчанию действия «index» (и «show» и т. Д.), Унаследованного от RestfulController. Это делается даже при отсутствии ранее существовавшего gson - предупреждения не выдаются.
Шаг 2: следующая остановка приложения (оно все еще кэширует предыдущее поведение по умолчанию).
Теперь перейдите в пустой каталог представления и создайте «index.gson», подобный этому, без модели
json ([1,2,3])
Затем перезапустите приложение в вашей IDE. Теперь, когда он запускается, он обнаруживает, что существует index.gson, и когда вы вызываете url 'http://localhost:8080/api/org', вы получаете отрендеренный json для статического списка [1,2,3]
шаг 3: представления gson используют статическую компиляцию под обложками и очень аккуратно, чтобы получить абсолютно правильное представление. Однако, в случае действия индекса RestfulController по умолчанию, список OrgRoleInstances выбирается из модели домена и передается в качестве подсветки для представления. если вы хотите, чтобы привязка данных к представлению работала, то, если ваш объект домена имеет тип T, вы получите List<T>
.
Внутренним значением по умолчанию в базе кода является то, что если вы получите List<T>
, возвращенный для ответа, модель данных в представлении предполагается равной List<T> <T>List
, т.е. в моем примере это будет
List<OrgRoleInstance> orgRoleInstanceList
в модели gson view. Теперь с исправленным index.gson (вы можете редактировать это без остановки / запуска сервера в режиме разработки)
import com.softwood.domain.OrgRoleInstance
model {
List<OrgRoleInstance> orgRoleInstanceList
}
json {
recordCount orgRoleInstanceList.size()
}
Теперь, когда вы получаете URL, вы получаете ответ json с размером списка:
{"recordCount":4}
Обратите внимание, что если вы добавите дополнительную переменную в вашу модель, как это:
import com.softwood.domain.OrgRoleInstance
model {
List<OrgRoleInstance> orgRoleInstanceList
Integer orgRoleInstanceCount
}
json {
recordCount orgRoleInstanceList.size()
size orgRoleInstanceCount
}
и измените index.gson для печати новой переменной Integer - тогда она не привязана к данным из ответного действия по умолчанию.
Ответ браузера выглядит так:
{"recordCount":4,"size":null}
Вариант 4: - посмотреть на вызов класса 'template'. Настроив модель, которая ожидает List<T>
, тип runTime, созданный с помощью привязки данных, является экземпляром «grails.orm.PagedResultList». Это тип Iterable.
Это очень запутанно читать в документации - однако на самом деле не ясно, если вы создадите файл с именем _<file>.gson
. Затем это обрабатывается в шаблоне gson-файла.
Это может иметь свою собственную модель / json, но поскольку все это статически компилируется, вы должны получить типы, точно совпадающие, и, где необходимо, объявлять «имя» переменной модели при вызове шаблона (либо через g.render, либо неявный tmpl.<file>
.
Когда вы вызываете шаблон из родительского представления, вы можете передать или итерируемый тип модели List<T>
или выполнить итерацию в родительском представлении и передать каждый <T>
шаблону. Вы должны убедиться, что тип модели, который вы передаете в tmpl, объявлен как тот же тип в модели tmpl.
например. предполагая List<T>
в родительском представлении, но <T>
в шаблоне, вам нужно вызвать tmpl для каждого элемента в списке. например
если у вас есть tmpl как этот "_orgRoleInstance.gson".
Если у вас есть родительское представление, подобное этому (обратите внимание на одиночное <T>
, объявленное и переменная модели с именем 'org'
import com.softwood.domain.OrgRoleInstance
model {
OrgRoleInstance org
}
json {
id org.id
name org.name
}
Тогда для родительского представления "index.gson" вам нужно что-то вроде этого, которое вызывает tmpl столько раз, сколько у вас есть записей в списке, но я должен был сказать платформе, что имя переменной модели tmpl равно 'org ', передав карту. Это будет отображаться так, как вы ожидаете.
import com.softwood.domain.OrgRoleInstance
model {
List<OrgRoleInstance> orgRoleInstanceList
Integer orgRoleInstanceCount
}
orgRoleInstanceList.each { OrgRoleInstance org ->
json tmpl.orgRoleInstance(org:org)
}
Если вы объявите переменную tmpl как «def org», она все равно будет работать, но поскольку она статически типизирована, переменная передается как статический экземпляр Object (тип времени выполнения корректен, но вы не можете просто получить доступ к свойствам как статическим типом является Object), и ему сложно разобраться в приведениях, необходимых для доступа к свойствам.
Если вы хотите, чтобы родительское представление передавало переменную модели List<T>
в tmpl, вы можете =, но вы должны убедиться, что переменная модели в tmpl равна List<T>
, иначе привязка данных не работает.
Теперь в шаблоне вы можете вызывать json и перебирать список
например. с таким измененным tmpl
import com.softwood.domain.OrgRoleInstance
model {
//OrgRoleInstance org
List<OrgRoleInstance> orgs
}
json {
id orgs.id
name orgs.name
}
и пересмотренное родительское представление для вызова tmpl:
import com.softwood.domain.OrgRoleInstance
model {
List<OrgRoleInstance> orgRoleInstanceList
Integer orgRoleInstanceCount
}
/*
orgRoleInstanceList.each { OrgRoleInstance org ->
json tmpl.orgRoleInstance(orgs:org)
}*/
//alternate approach
json tmpl.orgRoleInstance (orgs:orgRoleInstanceList)
Что визуализируется так:
{"id":[1,2,3,4],"name":["Vodafone","Cisco","Cisco","Acme"]}
Вы заметите, что существует единственное предложение json, поэтому оно сначала выполняет итерацию всех идентификаторов, а затем всех имен.
Если это не то, что вам нужно, вам придется перебирать список, чтобы выполнять каждый по очереди
т.е. модифицированный tmpl, подобный этому, будет перебирать каждую запись по очереди
import com.softwood.domain.OrgRoleInstance
model {
//OrgRoleInstance org
List<OrgRoleInstance> orgs
}
json (orgs) {OrgRoleInstance org ->
id org.id
name org.name
}
и выдайте это в браузере
[{"id":1,"name":"Vodafone"},{"id":2,"name":"Cisco"},{"id":3,"name":"Cisco"},{"id":4,"name":"Acme"}]
Для тех, кто смущен представлениями Grails, я надеюсь, что это показывает, как это работает между отображениями URL (вы можете использовать Gradle "urlMappingsReport", чтобы увидеть их) и показывает, какие URL сопоставлены с какими действиями. Затем вам нужно создать представления gson с тем же именем, что и у этих действий, и сконфигурировать ваши представления gson и все созданные вами tmpl, чтобы знать о неявном поведении, которое JsonView применяет при обработке вашего кода.