grails.converters.JSON except few properties - json

I am using grails-1.3.2 and hbase-0.2.4.
I have the following domain class:
class MyClass{
String val1
String val2
String val3
//----
}
class MyClassController{
def someAction = {
def myClass = new MyClass()
//----
String valAsJson = (myClass as JSON)
render valAsJson
}
}
My question is, is any short way render only part of properties(for example render all except val3 property) ?

You can do something like this :
def myClass = MyClass.get(1)
//include
render myClass.part(include:['val1', 'val2']) as JSON
//except
render job.part(except:['val2','val3']) as JSON
Bootstrap.groovy :
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor as Events
class BootStrap {
def grailsApplication
def excludedProps = [Events.ONLOAD_EVENT,
Events.BEFORE_DELETE_EVENT, Events.AFTER_DELETE_EVENT,
Events.BEFORE_INSERT_EVENT, Events.AFTER_INSERT_EVENT,
Events.BEFORE_UPDATE_EVENT, Events.AFTER_UPDATE_EVENT]
def init = { servletContext ->
grailsApplication.domainClasses.each{ domainClass ->
domainClass.metaClass.part= { m ->
def map= [:]
if(m.'include'){
m.'include'.each{
map[it]= delegate."${it}"
}
}else if(m.'except'){
m.'except'.addAll excludedProps
def props= domainClass.persistentProperties.findAll {
!(it.name in m.'except')
}
props.each{
map[it.name]= delegate."${it.name}"
}
}
return map
}
}
}
def destroy = {
}
}
If you know how to create our own plugin, then just create one plugin for this, so that you can use it across all the grails applications.

If you want to only include specific properties all the time, you would really want to use the ObjectMarshaller interface. See this article for more details.

If you simply want to render an instance of MyClass as JSON, excluding certain properties, here's a solution that uses the JSONBuilder class provided by Grails
import grails.web.JSONBuilder
class MyClassController{
def someAction = {
def myClass = new MyClass()
def builder = new JSONBuilder.build {
myClass.properties.each {propName, propValue ->
// Properties excluded from the JSON
def excludes = ['class', 'metaClass', 'val3']
if (!excludes.contains(propName)) {
setProperty(propName, propValue)
}
}
render(text: builder.toString(), contentType: 'application/json')
}
}

Or, you could just create a map of the properties you wanted, then encode them as JSON
Map m = [ 'val1', 'val2' ].inject( [:] ) { map, val -> map."$val" = a."$val" ; map }
render m as JSON
To exclude properties, you would need to do something like this (UNTESTED)
def exclude = [ 'val3' ]
Map m = new DefaultGrailsDomainClass( MyClass.class ).properties.findAll {
!( it.name in exclude )
}.inject( [:] ) { map, val ->
map."$val.name" = a."$val.name" ; map
}
render m as JSON

The JSON Exclusion Marshaller Plugin
I needed to solve this problem recently. I went ahead and packaged the solution into a plugin that allows you to easily exclude class properties from the JSON converter's output. It is available on the Grails Plugin Portal.
After you install the plugin, you will have access to a method on the grails.converters.JSON class called excludeFor*().
More extensive documentation can be found here: How to use the JSON Exclusion Marshaller
But basically it can be used as such:
import grails.converters.JSON
def json, resultTeachersWillSee, resultOtherStudentsWillSee
// Given a TestStudent Domain Class
def student = new TestStudent([
firstName: "Tobias",
lastName: "Funke",
gradePointAverage: 3.6,
studentID: "FS-210-7312",
socialSecurityNumber: "555-55-5555"
])
student.save(flush: true)
// When
JSON.excludeForTeachers(TestStudent, ['socialSecurityNumber', 'id', 'class'])
JSON.use('excludeForTeachers') {
json = new JSON(student)
}
resultTeachersWillSee = json.toString()
// Then
assert resultTeachersWillSee == '{"firstName":"Tobias",
"gradePointAverage":3.6, "lastName":"Funke",
"studentID":"FS-210-7312"}'
// And When
JSON.excludeForOtherStudents(TestStudent, ['gradePointAverage', 'studentID',
'socialSecurityNumber', 'id', 'class'])
JSON.use('excludeForOtherStudents') {
json = new JSON(student)
}
resultOtherStudentsWillSee = json.toString()
// Then
assert resultOtherStudentsWillSee == '{"firstName":"Tobias",
"lastName":"Funke"}'
JSON.excludeForTeachers(...) creates a named object marshaller called "excludeForTeachers". The marshaller excludes three properties of the student object from the resulting JSON output. the 'socialSecurityNumber' property is explicitly defined in the class, while the 'id' property was added by GORM behind the scenes. In any case, teachers don't need to see any of those properties.
The plugin is serving me well... I hope others find it helpful too.

Related

how to put pretty JSON into REST api

I am trying to build a REST service which accepts XML and convert it into JSON and call external Service which accepts JSON and put my JSON into it. I am able to put the json without pretty but I want to PUT the json in pretty format. Please suggest how to do, below is my code ...
package com.mypackge
import grails.converters.JSON
import grails.rest.RestfulController
import grails.plugins.rest.client.RestBuilder
class RestCustomerController extends RestfulController {
/*
static responseFormats = ['json', 'xml']
RestCustomerController() {
super(Customer)
}
*/
def index() {
convertXmlToJson()
}
def myJson = ''
def convertXmlToJson() {
def xml = ''' <Customer>
<customerid>9999999999999</customerid>
<ssn>8888</ssn>
<taxid>8888</taxid>
<address>
<addressline1>Yamber Ln</addressline1>
<addressline1>8664 SE</addressline1>
<city>CCCCC</city>
<state>CC</state>
<zipcode>97679</zipcode>
</address>
<firstname>Scott</firstname>
<middlename></middlename>
<lastname>David</lastname>
<account>
<accountno>576-294738943</accountno>
<accounttype>Lease</accounttype>
<accountsubtype></accountsubtype>
<accountstatus>complete</accountstatus>
<firstname>Scott</firstname>
<middlename></middlename>
<lastname>David</lastname>
<businessname></businessname>
<billingsystem>yoiuhn</billingsystem>
<brand></brand>
<plantype></plantype>
<billingaddress>
<addressline1>Yamber Ln</addressline1>
<addressline1>8664 SE </addressline1>
<city>CCCCC</city>
<state>CC</state>
<zipcode>97679</zipcode>
</billingaddress>
<job>
<jobid>8276437463728</jobid>
<jobstatus>SUCCESS</jobstatus>
</job>
</account>
</Customer>
'''.stripMargin()
// Parse it
def parsed = new XmlParser().parseText( xml )
def myId = parsed.customerid.text()
// Deal with each node:
def handle
handle = { node ->
if( node instanceof String ) {
node
}
else {
[ (node.name()): node.collect( handle ) ]
}
}
// Convert it to a Map containing a List of Maps
def jsonObject = [ (parsed.name()): parsed.collect { node ->
[ (node.name()): node.collect( handle ) ]
} ]
def json = new groovy.json.JsonBuilder(jsonObject) //.toPrettyString()
// Check it's what we expected
def mmyresp
try{
mmyresp = putRequest(myId,json)
}catch(Exception e) {
mmyresp = 'Please Validate JSON ....'
}
}
def putRequest(String id, JSON myJson) {
String url = "http://foo.com/customer/external/"+id
def rest = new RestBuilder()
def resp = rest.put(url){
contentType "application/json"
json{
myJson
}
}
return resp
}
}
The record is added in below format ...
{"Customer":[{"customerid":["9999999999999"]},{"ssn":["8888"]},
{"taxid":["8888"]},{"address":[{"addressline1":["Yamber Ln"]},
{"addressline1":["8664 SE"]},{"city":["CCCCC"]},{"state":["CC"]},{"zipcode":["97679"]}]},
{"firstname":["Scott"]},{"middlename":[]},{"lastname":["David"]},{"businessname":[]},
{"account":[{"accountno":["576-294738943"]},{"accounttype":["Lease"]},{"accountsubtype":[]},
{"accountstatus":["complete"]},{"firstname":["Scott"]},{"middlename":[]},{"lastname":["David"]},
{"businessname":[]},{"billingsystem":["yoiuhn"]},{"brand":[]},{"plantype":[]},
{"billingaddress":[{"addressline1":["Yamber Ln"]},{"addressline1":["8664 SE"]},
{"city":["CCCCC"]},{"state":["CC"]},{"zipcode":["97679"]}]},{"job":[{"jobid":["8276437463728"]},
,{"jobstatus":["SUCCESS"]}]}]}]}
But I want this to be inserted in pretty format. I tried .toPrettyString() but got casting exception when try to put as json. I am trying the REST services for the first time, not sure where I am doing wrong. Please suggest me on this.
You should set following field in you Config.groovy.
grails.converters.default.pretty.print = true
This will pretty print for both the xml and json.
you could optionally set it up for xml or json only like below:
For json:
grails.converters.json.pretty.print = true
For xml
grails.converters.xml.pretty.print = true
A sample of Config.groovy entry is:
environments {
development {
grails.converters.json.pretty.print = true
}
}
Hope it helps!!!
For Grails 4, try this:
def json = x as JSON
json.prettyPrint = true;
log.info(json.toString())

Grails JSON converter and JSONObject code breaks when moved to src/groovy

I am trying to move some code from a grails service file into src/groovy for better reuse.
import grails.converters.JSON
import org.codehaus.groovy.grails.web.json.JSONObject
class JsonUtils {
// seems like a clunky way to accomplish converting a domainObject
// to its json api like object, but couldn't find anything better.
def jsonify(obj, ArrayList removeableKeys = []) {
def theJson = obj as JSON
def reParsedJson = JSON.parse(theJson.toString())
removeableKeys.each { reParsedJson.remove(it) }
return reParsedJson
}
// essentially just turns nested JSONObject.Null things into java null things
// which don't get choked on down the road so much.
def cleanJson(json) {
if (json instanceof List) {
json = json.collect(cleanJsonList)
} else if (json instanceof Map) {
json = json.collectEntries(cleanJsonMap)
}
return json
}
private def cleanJsonList = {
if (it instanceof List) {
it.collect(cleanJsonList)
} else if (it instanceof Map) {
it.collectEntries(cleanJsonMap)
} else {
(it.class == JSONObject.Null) ? null : it
}
}
private def cleanJsonMap = { key, value ->
if (value instanceof List) {
[key, value.collect(cleanJsonList)]
} else if (value instanceof Map) {
[key, value.collectEntries(cleanJsonMap)]
} else {
[key, (value.class == JSONObject.Null) ? null : value]
}
}
}
but when I try to call jsonify or cleanJson from services I get MissingMethodExceptions
example call from grails service file:
def animal = Animal.read(params.animal_id)
if (animal) json.animal = JsonUtils.jsonify(animal, ['tests','vaccinations','label'])
error:
No signature of method: static org.JsonUtils.jsonify() is applicable for argument types: (org.Animal, java.util.ArrayList) values: [ ...]]\ Possible solutions: jsonify(java.lang.Object, java.util.ArrayList), jsonify(java.lang.Object), notify()
Also tried making the jsonify take an animal jsonify(Animal obj, ...) then it just said Possible solutions: jsonify(org.Animal, ...
The cleanJson method was meant to deal with JSONObject.Null things which have caused problems for us before.
example call:
def safeJson = JsonUtils.cleanJson(json) // json is request.JSON from the controller
error:
groovy.lang.MissingMethodException: No signature of method: static org.JsonUtils.cleanJson() is applicable for argument types: (org.codehaus.groovy.grails.web.json.JSONObject) values: [[...]]
Possible solutions: cleanJson(org.codehaus.groovy.grails.web.json.JSONObject)
All this code worked as it is when it was in service file. I am running grails 2.3.11 BTW
You've declared jsonify() and cleanJson() as instance methods and try to use them as static. Declare them as static and it should work:
class JsonUtils {
def static jsonify(obj, ArrayList removeableKeys = []) {
(...)
}
def static cleanJson(json) {
(...)
}
}
You need to define jsonify() and cleanJson() as static in order to call them statically.

How to access elements of JSON object in view.gsp to use as parameters in an image call?

I think I'm passing a JSON object from my controller to my view but I can't seem to access the discreet elements of the JSON. Please share your thoughts.
How much of the DisplayRecentPortlet is required, should I be
accessing the JSON elements there?
Am I really passing JSON?
How might I access the individual elements inside jsonObj to construct an
img tag in the view?
DisplayRecentContoller.groovy
package cssinstaviewer
import groovy.json.*
import grails.converters.JSON
class DisplayRecentController {
def view() {
def apiUrl = new URL("https://api.instagram.com/v1/users/STUFFTHATWORKS/media/recent/?client_id=STUFFTHATWORKS")
def doc = new JsonSlurper().parseText(apiUrl.text)
def jsonObj = new JsonBuilder(doc.data.images.low_resolution[0])
render(view:"view", model: [jsonObj: jsonObj as JSON])
}
}
DisplayRecentPortlet.groovy
package cssinstaviewer
import javax.portlet.*
class DisplayRecentPortlet {
def title = 'CSS Instagram Viewer'
def description = '''
CSS Instagram Viewer
'''
def displayName = 'CSS Instagram Viewer'
def supports = ['text/html':['view']]
//uncomment to declare events support
//def events = [publish: ["event-1"], process: ["event-2"]]
//uncomment to declare public render parameter support
//def public_render_params = ["prp-1","prp-2"]
// Used for liferay
// #see http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/anatomy-of-a-portlet
def liferay_display_category = "category.St. Scholastica"
def actionView = {
//TODO Define action phase for 'view' portlet mode
portletResponse.setRenderParameter("prp-1", "value-1");
}
def eventView = {
//TODO Define event phase for 'view' portlet mode.
def eventValue = portletRequest.event.value
}
def renderView = {
//TODO Define render phase for 'view' portlet mode.
//Return the map of the variables bound to the view,
//in this case view.gsp if it exists or render.gsp if not
['jsonObj':'jsonObj']
}
def resourceView = {
//TODO define resource phase for 'view' portlet mode.
//Render HTML as response
render {
html {
head()
body {
"Render me"
}
}
}
}
def actionEdit = {
//TODO Define action phase for 'edit' portlet mode
portletResponse.setEvent("event-1","event-1")
portletResponse.setPortletMode(PortletMode.VIEW)
}
def renderHelp = {
//TODO Define render phase for 'help' portlet mode
//Return the map of the variables bound to the view,
//in this case help.gsp if it exists or render.gsp if not
['mykey':'myvalue']
}
def doResource = {
//TODO Define handling for default resource URL handling method, independent of porlet mode
//Return the map of the variables bound to the view,
//in this case resource.gsp
['mykey':'myvalue']
}
//invoked by setting 'action' param in resourceURL (as an example) to 'doSomethingAjaxy'
def doSomethingAjaxy = {
//render JSON
render(contentType:"text/json") {
example(mykey:"myvalue")
}
}
//invoked by setting 'action' param in eventURL (as an example) to 'handleThisEvent'
def handleThisEvent = {
//render thisEvent.gsp
render(view:"thisEvent")
}
}
view.gsp
<%#page import="cssinstaviewer.DisplayRecentPortlet"%>
<%# taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<div>
<g:set var="instaImg" value="${jsonObj}" />
instImg = ${instaImg}
<hr />
jsonObj ${jsonObj}"
</div>
Output
instImg = {"content":{"height":306,"width":306,"url":"http://scontent-a.cdninstagram.com/hphotos-xap1/t51.2885-15/925254_1484378108464185_1325554272_a.jpg"}} jsonObj {"content":{"height":306,"width":306,"url":"http://scontent-a.cdninstagram.com/hphotos-xap1/t51.2885-15/925254_1484378108464185_1325554272_a.jpg"}}"
Well, I didn't need to send JSON from the controller I changed
render(view:"view", model: [jsonObj: jsonObj as JSON])
to
render(view:"view", model: [jsonObj: jsonObj])
and then I could address the elements separately by key name in the view.
<img src="${instaImg.content.url}" width="${instaImg.content.width}" height="${instaImg.content.height}">
It is a little peculiar to put a JSON object in the model like you are doing but that aside, I will make an attempt to address your specific question...
In your controller you could do something like this to add the image path to the model:
def view() {
def apiUrl = new URL("https://api.instagram.com/v1/users/STUFFTHATWORKS/media/recent/?client_id=STUFFTHATWORKS")
def doc = new JsonSlurper().parseText(apiUrl.text)
def jsonObj = new JsonBuilder(doc.data.images.low_resolution[0])
def imagePath = jsonObj.content.url
render(view:"view", model: [jsonObj: jsonObj as JSON, imagePath: imagePath])
}
And then in your view you could do something like this...
<img src="${imagePath}"/>
You could also do something like this...
<img src="${jsonObj.content.url}"/>
Is any of that the sort of thing you are looking for?
You should also take a look at http://grails.org/doc/latest/ref/Tags/img.html as a mechanism for generating the img tag.

Having trouble getting Writes to work with Scala Play

To begin with I would like to say sorry for long post, and I really appreciate those who still look into my problem.
I have a controller that should return a json-response with a structure like:
{
result: [
{
key: value,
key: value,
key: value,
key: [
{
key: value,
key: value,
key: value
},...
]
},....
]
}
However I have problems getting the Writes to work as I want.
Note. I will add comments under the line where I have problem.
object APIController extends Controller {
def feed() = Action {
val objects = repo.getObjects().toList
Ok(Json.toJson(Json.obj("result" -> Class_1.apply(objects).result)))
}
first off, if I don't make a Json.obj("result" -> List[objects]) the result key isn't shown in the JSON-result. If I add a Writer for that I get errors saying that the List[objects] must have a Writer. But if I write it like above it doesn't need a Writer for the List[objects]
case class Class_1 (result: Seq[Class_2])
object Class_1 {
def apply(objs: List[Object]) = {
var result:ListBuffer[Class_2] = ListBuffer[Class_2]()
for(obj <- objs) feedResult += Class_2.apply(code)
new Class_1(result.toList)
}
}
*this is where I would put the Writer for Class_1. But if I do this like
implicit val class1Writer = new Writes[Class_1] {
override def writes(o: Class_1): JsValue = Json.obj("result" -> o.result)
} I get the problems I mentioned earlier, that I suddenly need a Writes for a List[objects] of that type*
case class Class_2 (id: Long, id2: Long, listOfStuff: Seq[Class_3])
object Class_2 {
def apply(obj: Object) = {
var items: ListBuffer[Class_3] = ListBuffer[Class_3]()
for(obj1 <- obj.getListOfStuff()) items += Class_3.apply(obj1)
new Class_2(obj.firstID, obj.secID, items.toList)
}
}
implicit val class2Writes = new Writes[Class_2] {
override def writes(o: Class_2): JsValue = {
Json.obj(
"id" -> o.id,
"id2" -> o.id2,
"items" -> o.listOfStuff
)
}
}
*the "items" -> o.listOfStuff says it needs a Writes for a List[types in the list] but I have a Writes for the objects in the list (Class_3) and I don't need a Writes for when serializing a list of objects from Class_2, why is it behaving like this?*
case class Class_3 (id: Long, text: String)
object Class_3 {
def apply(obj: Object) = {
new Class_3(obj.id, obj.funnyText)
}
}
implicit val class3Writer = new Writes[Class_3] {
override def writes(o: Class_3): JsValue = {
Json.obj(
"id" -> o.id,
"text" -> o.text
)
}
}
}
The error I get from this code is:
No Json deserializer found for type Seq[Class_3]. Try to implement an implicit Writes or Format for this type.
[error] "items" -> o.listOfStuff
[error] ^
If I remove this line in the Writes it compiles and works.
And I think that's weird since the first list I serialize doesn't have a Writer, only for the objects in the list.
Does anyone know why it behaves like this?
What should I do to accomplish what I'm after? (I hope you see what I'm trying to do)
Thanks in advance.
Just put the implicit val class3Writer ahead of class2Writes

grails render collection as single json object

For example I have next Domain class
User{
String name
}
Also I have 2 objects of this class
new User(name: "John").save()
new User(name: "Alex").save()
How should look "list" action in UserController to represent User.list() in JSON format like this
{1: "John", 2: "Alex"}
Let me be more precise. I want something like this:
UserController{
def list = {
render(contentType: "text/json") {
User.list().each {user->
user.id = user.name
}
}
}
But sadly this isn't working.
Try the array structure,
def list = {
render(contentType: "text/json") {
results = array {
User.list().each {user->
result "${user.id}" : "${user.name}"
}
}
}
}
I couldn't find solution with JSONBuilder API. Because of that I made my solution with help of org.codehaus.jackson.
response.setContentType("text/json")
JsonGenerator g = jsonFactory.createJsonGenerator(response.getOutputStream())
g.writeStartObject()
for (user in users) {
g.writeStringField(user.id.toString(), user.name)
}
g.writeEndObject()
g.close()
When I want to encode something as JSON in grails, I put everything in maps:
render ['1':john.name, '2':alex.name] as JSON
Starting from #aldrin answer, a correction is needed for GRAILS3 json rendering because array directive is no more working (and is more correct 'application' instead of 'text'), so the solution must be
def list = {
def ulist = User.list()
render(contentType: "application/json") {
results(ulist) { user ->
userid user.id
username user.name
}
}
}