I've just started working with Grails 2.4.4 and I'm trying to create a REST api which outputs JSON but I have a problem with it when it comes to render the object as JSON. Listed below are the domain class, controller and the objectmarshall.
Domain class :
#Resource(uri='/users')
class User {
List contacts;
String name;
String password;
static hasMany=[contacts:Contact]
static constraints = {
}
static mapping = {
contacts lazy: false
}
}
Controller :
class UserController {
def index() {
//json
render User.getAll() as JSON
}
Boostrap groovy :
class BootStrap {
def init = { servletContext ->
JSON.registerObjectMarshaller(User) {
def output = [:]
output['id'] = it.id
output['name'] = it.name
output['contacts'] = it.contacts
return output;
}
JSON.registerObjectMarshaller(Contact) {
def output = [:]
output['id'] = it.id;
output['name'] =it.name;
output['phoneNumber'] = it.phoneNumber;
output['userId'] = it.user.id;
return output;
}
}
}
After I run my application the first time,it returns XML instead of JSON but if I make a new change that will generate a hot deploy(for e.g if I add a comment) it will generate JSON.
What am I missing?
Try to declare content type with render(contentType:"application/json")
I find creating a map of what you want and rendering that is much simpler.
def renderMe = [name: "Joe"]
render renderMe as JSON
Related
I am trying to create a custom JsonAdapter for my JSON data that would bypass the serialization of specific field. Following is my sample JSON:
{
"playlistid": 1,
"playlistrows": [
{
"rowid": 1,
"data": {
"123": "title",
"124": "audio_link"
}
}
]
}
The JSON field data in above have dynamic key numbers, so I want to bypass this data field value and return JSONObject.
I am using RxAndroid, Retrofit2 with Observables. I have created a service class:
public static <S> S createPlaylistService(Class<S> serviceClass) {
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(baseURL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClientBuilder.build())
.addConverterFactory(MoshiConverterFactory.create());
return builder.build().create(serviceClass);
}
I am calling this service using observable like this:
#GET("http://www.mylink.com/wp-json/subgroup/{subgroupId}/playlist/{comboItemId}")
Observable<Playlist> getPlaylist(#Path("subgroupId") int subgroupId, #Path("comboItemId") int comboItemId);
Then I run it like this:
ServiceBuilder.createPlaylistService(FHService.class).getPlaylist(123, 33);
My Pojo classes look like this:
public class Playlist {
#Json(name = "playlistid")
public Long playlistid;
#Json(name = "playlistrows")
public List<Playlistrow> playlistrows = null;
}
public class Playlistrow {
#Json(name = "rowid")
public Long rowid;
#Json(name = "data")
public Object data;
}
The problem is it would return a data value in this format:
{
123=title,
124=audio_link
}
which is invalid to parse as JSONObject.
I have Googled a lot and have also checked some Moshi example recipes but I had got no idea about how to bypass this specific field and return valid JSONObject, since I am new to this Moshi library.
I've got a controller defined:
class FamilyController extends RestfulController {
def springSecurityService
def familyService
static responseFormats = ['json']
FamilyController() {
super(Family)
}
#Override
protected List<Family> listAllResources(Map params) {
def user = springSecurityService.loadCurrentUser()
return Family.findAllByUser(user)
}
#Override
protected Family createResource() {
//def instance = super.createResource()
//TODO: Should be able to run the above line but there is an issue GRAILS-10411 that prevents it.
// Code from parent is below, as soon as the jira is fixed, remove the following lines:
def p1 = params
def p2 = getObjectToBind()
Family instance = resource.newInstance()
bindData instance, getObjectToBind()
/*
* In unit testing, the bindData operation correctly binds the params
* data to instance properties. When running with integration tests,
* this doesn't happen and I have to do the property assignment myself.
* Not sure if this breaks anything elsewhere.
*/
instance.properties = p1
//Code from super ends here
familyService.addFamilyToUser(instance)
return instance
}
#Override
protected Family queryForResource(Serializable id) {
def inst = familyService.safeGetFamily(Long.parseLong(id))
return inst
}
public create()
{
Family r = this.createResource()
render r as JSON
}
public index()
{
render this.listAllResources(params) as JSON
}
}
And for pedantic purposes (I'm new to grails/groovy and I want to watch the controller work), I want to take a look at actual results from the various actions in the controller. So I built an integration test:
#Mock([AdultPlanning, Primary, Secondary, Child, Family, Tenant, User])
class FamilyControllerIntegrationSpec extends IntegrationSpec {
Tenant tenant
User user
FamilyController controller = new FamilyController()
FamilyService familyService = new FamilyService()
def setupSpec() {
tenant = new Tenant(name: 'MMSS')
user = new User(subject: "What is a subject?", identityProvider: "http://google.com/")
tenant.addToUsers(user)
tenant.save()
user.save() // The tenant save should propagate to the user, this is to make sure.
/*
* Set up the custom marshalling code for family objects.
*/
new AdultPlanningMarshaller()
new ChildMarshaller()
new FamilyMarshaller()
new FamilyTypeEnumMarshaller()
return
}
def cleanup() {
}
void "Create a Family"() {
when:
def mSpringSecurityService = mockFor(SpringSecurityService)
/*
* I expect to make n calls (1..3) on loadCurrentUser in this test.
*/
mSpringSecurityService.demand.loadCurrentUser(1..3) { -> return user }
controller.springSecurityService = mSpringSecurityService.createMock()
controller.request.contentType = 'text/json'
controller.index()
def json
try {
json = controller.response.json
}
catch (e)
{
json = null // action didn't emit any json
}
def text = controller.response.text
then:
!json
text == "[]"
when:
controller.params.name = "Test"
controller.params.typeOfFamily = FamilyTypeEnum.SINGLE
controller.familyService.springSecurityService = controller.springSecurityService //mSpringSecurityService.createMock()
/*
* I've tried both GET (default) and POST to get bindData to pick up parameters properly,
* with no joy.
*/
// controller.request.method = "POST"
controller.request.contentType = 'text/json'
controller.create()
try {
json = controller.response.json
}
catch (e)
{
json = null // action didn't emit any json
}
text = controller.response.text
then:
1 == 1
when:
controller.index()
json = controller.response.json
then:
json.name == "Test"
}
}
So in the first when block, I get no families listed (and empty json object and a string of [] in the response), which I expected, but until I actually implemented the index action in code, I got no json back from the action which I didn't expect.
On to the 2nd when. I create a family (problems there with dataBinding but I tossed in a workaround, first things first) and the family gets created and saved, but nothing comes back in the response. I've used the debugger and I see the JSON marshaller triggering for the newly created family so SOMETHING is rendering JSON but where is it going?.
So I'm clearly doing something wrong (or I don't understand what actions are supposed to do in terms of what they return for passing on to the web client) but I'm new in this domain and don't have a clue as to what was done wrong.
I want to teach my domain class to automatically convert the results of JSON.parse(someJSON) into a member that is also a custom domain class.
Given these domain classes:
class Person {
Long id
String name
static hasMany = [aliases: PersonAlias]
}
class PersonAlias {
Person person
Long id
String name
}
And this JSON representing a Person with some PersonAliases:
{
"id":20044397,
"name":"John Smith",
"aliases":[{"id":13376,"name":"Johnny Smith"},{"id":13377,"name":"J. Smith"}]
}
I want to keep the controller simple like:
class PersonController {
def saveViaAjax = {
def props = JSON.parse(params.JSON)
Person p = Person.get(props.id)
p.properties = props
p.save(flush: true)
}
}
But sadly I get this error:
Failed to convert property value of type
'org.codehaus.groovy.grails.web.json.JSONArray' to required type
'java.util.Set' for property 'aliases'; nested exception is
java.lang.IllegalStateException: Cannot convert value of type
[org.codehaus.groovy.grails.web.json.JSONObject] to required type
[heavymeta.PersonAlias] for property 'aliases[0]': no matching editors
or conversion strategy found
So, I want to teach my domain class to how to convert the JSON data into PersonAlias instances automatically. I'd like to avoid formatting the data in the controller before passing it to the Domain object. How do I accomplish these goals?
You can use the bindUsing annotation and provide your custom binding code to convert the json to the property being bound.
class Person {
Long id
String name
#BindUsing({obj, source ->
List retVal = []
def aliases = source['aliases']
if(aliases) {
aliases.each {
retVal << new PersonAlias(name:it.name)
}
}
return retVal
})
List<PersonAlias> aliases
static hasMany = [aliases: PersonAlias]
}
I think this plugin: https://github.com/pedjak/grails-marshallers might do what you're looking for? I have not tried it myself though.
I also encountered this problem - I did my best to document the fix on my website - See http://dalelotts.com/software-architect/grails
In general the solution is to convert the JSON to a parameter map that can be used for data binding. More info on the site, including an annotation driven DomainClassMarshaller for JSON
protected Object readFromJson(Class type, InputStream entityStream, String charset) {
def mapper = new ObjectMapper();
def parsedJSON = mapper.readValue(entityStream, typeRef);
Map<String, Object> map = new HashMap<>();
parsedJSON.entrySet().each {Map.Entry<String, Object> entry ->
if (List.isAssignableFrom(entry.getValue().getClass())) {
List values = (List) entry.getValue();
int limit = values.size()
for (int i = 0; i < limit; i++) {
final theValue = values.get(i)
map.put(entry.key + '[' + i + ']', theValue)
appendMapValues(map, theValue, entry.key + '[' + i + ']' )
}
} else {
map.put(entry.key, entry.value);
}
}
def result = type.metaClass.invokeConstructor(map)
// Workaround for http://jira.codehaus.org/browse/GRAILS-1984
if (!result.id) {
result.id = idFromMap(map)
}
result
}
private void appendMapValues(Map<String, Object> theMap, Object theValue, String prefix) {
if (Map.isAssignableFrom(theValue.getClass())) {
Map<String, Object> valueMap = (Map<String, Object>) theValue;
for (Map.Entry<String, Object> valueEntry : valueMap.entrySet()) {
theMap.put(prefix + '.' + valueEntry.key, valueEntry.value)
appendMapValues(theMap, valueEntry.value, prefix + '.' + valueEntry.key)
}
}
}
My Problem is: I don't want to return the whole model object structure and datas in a json response. renderJSON() returns everything from the model in the response.
So I thought the best way would be to use *.json templates. Ok and now I have a List and I don't now how the syntax in the *.json-template must be.
Part of Controller:
List<User> users = User.find("byActive", true).fetch();
if (users != null) {
render(users);
}
"User/showAll.json" (template):
//something like foreach( User currentUser in users )
{
"username": "${currentUser.name}",
"userdescr": "${currentUser.description}"
}
For a single user it's no problem, I got it, but how does it look like for a List of users inside the json template?
There is another solution to your problem without using .json template.
"renderJSON()" has a variation takes JsonSerializer as parameter, so you can define your own serializer which implements JsonSerializer, and decide what part of model object to be sent in the response. Then you could invoke renderJSON() to return JSON object in the controller.
Example:
public class UserSerializer implements JsonSerializer<User> {
public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new LocalExclusionStrategy()).create();
return gson.toJsonTree(src);
}
public static class LocalExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
// add exlusion rules here:
// exclude all fields whose name is not "name" or "description"
return !f.getName().toLowerCase().equals("name")
&& !f.getName().toLowerCase().equals("description");
}
}
}
In your controller:
List<User> users = User.find("byActive", true).fetch();
renderJSON(users, new UserSerializer());
Play framework utilizes Google's GSON library for json serialization/deserialization
You can find more info of GSON here
[
#{list users}
{
"username": "${_.name}",
"userdescr": "${_.description}"
} #{if !_isLast},#{/if}
#{/list}
]
Check http://www.playframework.org/documentation/1.2.4/tags#list for more information
If I have a simple object such as
class Person {
String name
Integer age
}
I can easily render it's user-defined properties as JSON using the JSONBuilder
def person = new Person(name: 'bob', age: 22)
def builder = new JSONBuilder.build {
person.properties.each {propName, propValue ->
if (!['class', 'metaClass'].contains(propName)) {
// It seems "propName = propValue" doesn't work when propName is dynamic so we need to
// set the property on the builder using this syntax instead
setProperty(propName, propValue)
}
}
def json = builder.toString()
This works fine when the properties are simple, i.e. numbers or strings. However for a more complex object such as
class ComplexPerson {
Name name
Integer age
Address address
}
class Name {
String first
String second
}
class Address {
Integer houseNumber
String streetName
String country
}
Is there a way that I can walk the entire object graph, adding each user-defined property at the appropriate nesting level to the JSONBuilder?
In other words, for an instance of ComplexPerson I would like the output to be
{
name: {
first: 'john',
second: 'doe'
},
age: 20,
address: {
houseNumber: 123,
streetName: 'Evergreen Terrace',
country: 'Iraq'
}
}
Update
I don't think I can use the Grails JSON converter to do this because the actual JSON structure I'm returning looks something like
{ status: false,
message: "some message",
object: // JSON for person goes here
}
Notice that:
The JSON generated for the ComplexPerson is an element of a larger JSON object
I want to exclude certain properties such as metaClass and class from the JSON conversion
If it's possible to get the output of the JSON converter as an object, I could iterate over that and remove the metaClass and class properties, then add it to the outer JSON object.
However, as far as I can tell, the JSON converter only seems to offer an "all or nothing" approach and returns it output as a String
I finally figured out how to do this using a JSONBuilder, here's the code
import grails.web.*
class JSONSerializer {
def target
String getJSON() {
Closure jsonFormat = {
object = {
// Set the delegate of buildJSON to ensure that missing methods called thereby are routed to the JSONBuilder
buildJSON.delegate = delegate
buildJSON(target)
}
}
def json = new JSONBuilder().build(jsonFormat)
return json.toString(true)
}
private buildJSON = {obj ->
obj.properties.each {propName, propValue ->
if (!['class', 'metaClass'].contains(propName)) {
if (isSimple(propValue)) {
// It seems "propName = propValue" doesn't work when propName is dynamic so we need to
// set the property on the builder using this syntax instead
setProperty(propName, propValue)
} else {
// create a nested JSON object and recursively call this function to serialize it
Closure nestedObject = {
buildJSON(propValue)
}
setProperty(propName, nestedObject)
}
}
}
}
/**
* A simple object is one that can be set directly as the value of a JSON property, examples include strings,
* numbers, booleans, etc.
*
* #param propValue
* #return
*/
private boolean isSimple(propValue) {
// This is a bit simplistic as an object might very well be Serializable but have properties that we want
// to render in JSON as a nested object. If we run into this issue, replace the test below with an test
// for whether propValue is an instanceof Number, String, Boolean, Char, etc.
propValue instanceof Serializable || propValue == null
}
}
You can test this by pasting the code above along with the following into the grails console
// Define a class we'll use to test the builder
class Complex {
String name
def nest2 = new Expando(p1: 'val1', p2: 'val2')
def nest1 = new Expando(p1: 'val1', p2: 'val2')
}
// test the class
new JSONSerializer(target: new Complex()).getJSON()
It should generate the following output which stores the serialized instance of Complex as the value of the object property:
{"object": {
"nest2": {
"p2": "val2",
"p1": "val1"
},
"nest1": {
"p2": "val2",
"p1": "val1"
},
"name": null
}}
In order for the converter to convert the whole object structure you need to set a property in the config to indicate that, otherwise it will just include the ID of the child object, so you need to add this:
grails.converters.json.default.deep = true
For more information go Grails Converters Reference.
However, like you mentioned it in the comment above it is all or nothing, so what you can do is create your own marshaller for your class. I had to do this before because I needed to include some very specific properties, so what I did was that I created a class that extends org.codehaus.groovy.grails.web.converters.marshaller.json.DomainClassMarshaller. Something like:
class MyDomainClassJSONMarshaller extends DomainClassMarshaller {
public MyDomainClassJSONMarshaller() {
super(false)
}
#Override
public boolean supports(Object o) {
return (ConverterUtil.isDomainClass(o.getClass()) &&
(o instanceof MyDomain))
}
#Override
public void marshalObject(Object value, JSON json) throws ConverterException {
JSONWriter writer = json.getWriter();
Class clazz = value.getClass();
GrailsDomainClass domainClass = ConverterUtil.getDomainClass(clazz.getName());
BeanWrapper beanWrapper = new BeanWrapperImpl(value);
writer.object();
writer.key("class").value(domainClass.getClazz().getName());
GrailsDomainClassProperty id = domainClass.getIdentifier();
Object idValue = extractValue(value, id);
json.property("id", idValue);
GrailsDomainClassProperty[] properties = domainClass.getPersistentProperties();
for (GrailsDomainClassProperty property: properties) {
if (!DomainClassHelper.isTransient(transientProperties, property)) {
if (!property.isAssociation()) {
writer.key(property.getName());
// Write non-relation property
Object val = beanWrapper.getPropertyValue(property.getName());
json.convertAnother(val);
} else {
Object referenceObject = beanWrapper.getPropertyValue(property.getName());
if (referenceObject == null) {
writer.key(property.getName());
writer.value(null);
} else {
if (referenceObject instanceof AbstractPersistentCollection) {
if (isRenderDomainClassRelations(value)) {
writer.key(property.getName());
// Force initialisation and get a non-persistent Collection Type
AbstractPersistentCollection acol = (AbstractPersistentCollection) referenceObject;
acol.forceInitialization();
if (referenceObject instanceof SortedMap) {
referenceObject = new TreeMap((SortedMap) referenceObject);
} else if (referenceObject instanceof SortedSet) {
referenceObject = new TreeSet((SortedSet) referenceObject);
} else if (referenceObject instanceof Set) {
referenceObject = new HashSet((Set) referenceObject);
} else if (referenceObject instanceof Map) {
referenceObject = new HashMap((Map) referenceObject);
} else {
referenceObject = new ArrayList((Collection) referenceObject);
}
json.convertAnother(referenceObject);
}
} else {
writer.key(property.getName());
if (!Hibernate.isInitialized(referenceObject)) {
Hibernate.initialize(referenceObject);
}
json.convertAnother(referenceObject);
}
}
}
}
}
writer.endObject();
}
...
}
That code above is pretty much the same code as it is DomainClassMarshaller, the idea would be that you add or remove what you need.
Then in order for Grails to use this new converter what you need is to register it in the resources.groovy file, like this:
// Here we are regitering our own domain class JSON Marshaller for MyDomain class
myDomainClassJSONObjectMarshallerRegisterer(ObjectMarshallerRegisterer) {
converterClass = grails.converters.JSON.class
marshaller = {MyDomainClassJSONMarshaller myDomainClassJSONObjectMarshaller ->
// nothing to configure, just need the instance
}
priority = 10
}
As you can see this marshaller works for a specific class, so if you want to make more generic what you can do is create a super class and make your classes inherit from that so in the support method what you do is say this marshaller support all the classes that are instances of that super class.
My suggestion is to review the grails code for the converters, that will give you an idea of how they work internally and then how you can extend it so it works the way you need.
This other post in Nabble might be of help too.
Also, if you need to do it for XML as well then you just extend the class org.codehaus.groovy.grails.web.converters.marshaller.xml.DomainClassMarshaller and follow the same process to register it, etc.