Exclude properties when rendering object as json in grails 2.4.0 - json

I don't understand how can i exclude some properties when rendering an object as JSON in a controller (not a RESTController but a classic Controller).
I have this resources.groovy:
// Place your Spring DSL code here
import grails.rest.render.json.JsonRenderer
import com.appromocodes.Promocode
import com.appromocodes.ResponseStatus
import grails.rest.render.json.JsonCollectionRenderer
beans = {
responseStatusRenderer(JsonRenderer, ResponseStatus) {
excludes = ['enumType']
}
promocodeRenderer(JsonRenderer, Promocode) {
excludes = ['class', 'id', 'project']
}
}
Within my controller i tried in my action something like:
respond p as JSON
but this still gives me all the fields (also class, id and project fields).
What should i do?

The proper way to handle this is to register a custom JSON marshaller for your object. Begin by creating a new Marshaller in src/groovy/packageName/marshallers/PromocodeMarshaller.groovy with the following contents:
import packageName.Promocode
import grails.converters.JSON
class PromocodeMarshaller {
void register() {
JSON.registerObjectMarshaller(Promocode) { promocode ->
return [
id: promocode?.id,
// all the fields you'd like to return
// in your JSON object
]
}
}
}
Then, inside of your Bootstrap.groovy file, include the following:
import packageName.marshallers.PromocodeMarshaller
def promodcodeMarshaller = new PromocodeMarshaller()
promocodeMarshaller.register()
For a full description, see this article.

Related

Is there any way to use typescript generic class instance or methods?

Specifically, I'm trying to improve deserialization in my Angular project. I have a base HTTP class that abstracts away some of the universal aspects of interacting with my REST service:
import { HttpClient, HttpParams } from '#angular/common/http';
export class BaseHttpService {
...
protected get<T>(url: string, params: object = {}, options: object = {}): Observable<any> {
// some operations every get request needs to do
return this.httpClient.get<T>(url, { params: httpParams, ...options });
}
...
And I'm using the cerialize library to handle object deserialization, specifically for Java Date (millisecond since epoch format) conversion to Javascript Date, and occasionally some helper methods that I want stored on the class (as opposed to just taking the JSON in through a Typescript interface). So my use of this method always looks something like:
import { Deserialize } from 'cerialize';
import { ModelA } from './models';
export class SomeControllerService {
...
constructor(private baseHttpService: BaseHttpService) {}
someMethod<ModelA>(): Observable<ModelA> {
return this.get<ModelA>().pipe(map(response => Deserialize(response, ModelA)));
}
...
What I want to do is remove the need for .pipe(map(... which ends up being in every single method that returns a REST response object and is basically the same, just with a different model depending on the endpoint. In Java, I believe you could use the generic in the base service as an input to the Deserialize function like (response => Deserialize(response, T.class)). I also want to avoid adding another parameter to the base get function. Per my title, I could also see a solution that refactors to calling a function on the generic, that the individual models could implement specific deserialization logic in, but again, since the generic is just a type, that doesn't seem possible. Am I just running up to a limitation of Typescript?

Spring SCALA return list in RestController

How can I return a SCALA list or sequence in Sprint #RestController. List return value is not being serialized properly.
The result is:
[GET] http://localhost:9090/devices
{"empty":false,"traversableAgain":true}
Do I need to import Jackson ObjectMapper com.fasterxml.jackson for proper REST get result serialization on a list?
My RestController looks like this:
#RestController
class DeviceController {
var devices = Set[Device]()
#RequestMapping(value = Array("/devices"), method = Array(RequestMethod.GET))
def accounts() : List[Device] = devices.toList
}
Spring was NOT designed with SCALA in mind - hence it cannot handle SCALA lists properly. Nor can it handle Seq[Device].
Just use SCALA's JavaConvertes package to easliy convert SCALA list to JAVA list.
import scala.collection.JavaConverters._
#RestController
class DeviceController {
var devices = Set[Device]()
#RequestMapping(value = Array("/devices"), method = Array(RequestMethod.GET))
def accounts() : java.util.List[Device] = {
devices.toList.asJava
}
}
and the result will be:
[GET] http://localhost:9090/devices
[{"name":"first device"},{"name":"second device"}]
Remember to change the result type to: java.util.List[Device]

Grails 3 JSON Views custom rendering one-to-many associations

Okay I have tried everything I know to custom render a one-to-many association with JSON views and failed miserably.
Here's what part of my ticket domain looks like (All good here) ...
class TttTicket {
String title
String number
String description
TttUser assignee
String priority
String status
TttUser creator
Date dateCreated
Date lastUpdated
static mappedBy = [subscribers : 'none', creator:'none']
static belongsTo = [project:TttProject, creator:TttUser]
static hasMany = [subscribers: TttUser]
... blar blar
}
Here's my associated gson rendering template...
import ttt_api_server.TttTicket
model {
TttTicket tttTicket
}
json g.render(tttTicket, [excludes:['creator','subscribers','project']]){
creator {
id tttTicket.creator.id
name tttTicket.creator.name
email tttTicket.creator.email
}
project{
id tttTicket.project.id
name tttTicket.project.name
}
}
... which is working nicely so far. I want to now restricted the output of the properties for each of the subscribers.
How do I rotate over these? For example...
import ttt_api_server.TttTicket
model {
TttTicket tttTicket
}
json g.render(tttTicket, [excludes:['creator','subscribers','project']]){
creator {
id tttTicket.creator.id
name tttTicket.creator.name
email tttTicket.creator.email
}
subscibers g.render(){
tttTicket.subscribers.each {sub ->
return {
name sub.name
}
}
}
project{
id tttTicket.project.id
name tttTicket.project.name
}
}
This does not seem to be documented anywhere. I would like to controller the JSON out put of each subscriber at this level. Not at the domain level as I may need to change the property output depending on my JSON requirements.
Please help :-(
If I understand this correctly, when you do a REST call on ticket, you wanted the domain object that is inside the hasMany relationship to show a specified properties (in this case its subscribers). But when you call directly on the subscribers, you wanted all the properties.
You can create a map first and then render the objects based on the map created (have a look at this).
In your case:
import ttt_api_server.TttTicket
model {
TttTicket tttTicket
}
json g.render(tttTicket, [excludes:['creator','subscribers','project']]){
creator {
id tttTicket.creator.id
name tttTicket.creator.name
email tttTicket.creator.email
}
def s = tttTicket.subsribers.collect {
[
id: it.id,
name: it.name
]
}
subscribers s //this part is doing the rendering
project{
id tttTicket.project.id
name tttTicket.project.name
}
}
If this is the way you want this object to be rendered whenever it's rendered as JSON, I suggest implementing ObjectMarshaller<JSON>:
class TttTicketMarshaller implements ObjectMarshaller<JSON> {
#Override
boolean supports(Object object) {
return object instanceof TttTicket
}
#Override
void marshalObject(Object object, JSON converter) throws ConverterException {
TttTicket ticket = (TttTicket)object
def jsonWriter = converter.writer
jsonWriter.object()
jsonWriter.key("title")
jsonWriter.value(ticket.title)
//other fields
jsonWriter.key("creator")
jsonWriter.object()
jsonWriter.key("id")
jsonWriter.value(ticket.creator.id)
//other creator fields
jsonWriter.endObject()
}
}
I'm not 100% sure about the object() and endObject() functionality, but that appears to be the correct use.
Once you have this, register this in your BootStrap.groovy:
def init = { servletContext ->
JSON.registerObjectMarshaller(new TttTicketMarshaller())
}
With all this in place, you can simply use
render myTicket as JSON
to return the JSON representation of the object as defined by your marshaller

Grails parse JSON into domain class

Hi Say that I have a domain class
class Book{
static hasOne=[author:Author]
long id
String name
}
class Author {
static hasMany=[books:Book]
long id
String name
}
I have a json object sent in. Can I jus do a new Book(Json) and not manually set the property?
Using the built-in Grails JSON converter makes this easier
import grails.converters.JSON
class BookController {
def save = {
def book = new Book(JSON.parse(yourJson))
book.save(flush:true)
}
}
In the code what's happening (we're parsing a JSON object and setting the properties on the Book entity and saving
use the JsonBinder:
def json = request.JSON;
def book= new Book();
JsonBinder.bindJSON(book, json);
dont forget to import these packages:
import grails.converters.JSON;
import com.ocom.grails.JsonBinder;

JSON output of a view in Grails

Ok, I have a very simple app created in Grails.
I have a generated domain class (Person) and its generated controller, using the automatic Grails scaffold:
package contacts
class PersonController {
def scaffold = Person
}
Now I'd like to get a JSON representation of a Person object.
Do I have to change the view or the controller? And how?
Thank you.
Add the following to your controller:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
def personList = Person.list(params)
withFormat {
html {
[personInstanceList: personList, personInstanceTotal: Person.count()]
}
json {
render personList as JSON
}
}
}
This should support both your scaffolding and the JSON output.
You can access the scaffolding as:
http://localhost:8080/contacts/person/list
You can access the Person list as json with:
http://localhost:8080/contacts/person/list?format=json
There are other ways to do it too, but I like doing it this way to leave the scaffolding around for testing.