Grails JSON field missing - json

I need to create a JSON response in my application that will contain the object's equivalent + additional fields. Here's how the show() method looks like:
def show(Long id) {
verifyUserLoggedIn()
ScBusinessProcess scBusinessProcess = ScBusinessProcess.get(id)
BusinessProcess businessProcess = BusinessProcessTranslator.toREST(scBusinessProcess)
businessProcess.questions = getQuestions(scBusinessProcess)
businessProcess.rate = getUserRate();
businessProcess.totalSteps = calculateTotalSteps(scBusinessProcess);
businessProcess.usersCurrentStep = 1;
respond businessProcess
}
The toREST() method copies ScBusinessProcess domain class object into a new BusinessProcess Java object. Next ones are the fields I'm adding manually. So far, everything's been working except the last field that's not being shown in the response (debugger claims that the newly created object contains the field and its passed value). Here's my toREST method:
public static BusinessProcess toREST(ScBusinessProcess scBusinessProcess) {
if (scBusinessProcess == null) return null;
return new BusinessProcess(
scBusinessProcess.id(),
scBusinessProcess.getName(),
null,
scBusinessProcess.getDescription(),
scBusinessProcess.getPromoted(),
scBusinessProcess.getLikedCount(),
scBusinessProcess.getDislikedCount(),
0,
0,
0
);
}
And below, the returned JSON:
class "rest.BusinessProcess"
description "Business Process Description"
dislikes 0
id 1
likes 1
name "BusinessProcessOne"
promoted false
questions []
rate 1
totalSteps 0
The usersCurrentStep is missing. What can be the problem? If I need to put more code just say. Also, the Grails version that the application is built on is 3.1.9.

What is the structure of the BusinessProcess class? You can generate JSON of any structure you like if you have the right marshaller - see this link to know more about object marshallers -
http://docs.grails.org/3.0.17/guide/webServices.html#objectMarshallers

Related

Display two domain object values as one JSON output

I am working on grails and I have a use case with two domain classes class A and class B which are independent classes.I want the class A and class B values in one JSON view.
class A
String name,
String age
class B
String report
In Bootstrap.groovy file i have written the below format to display the values in JSON view.
JSON.registerObjectMarshaller(A) {it->
def result = [:]
result['name'] = it.name
result['age'] = it.age
result['report'] = it.report //It throws an error no such property: report for class A.How to write it in a proper JSON format to retrieve the report value?
return result
}
From the documentation
Groovy comes with integrated support for converting between Groovy
objects and JSON. The classes dedicated to JSON serialisation and
parsing are found in the groovy.json package.
For example you could use groovy.json.JsonBuilder
In your example you could try this
Having classes A and B
class A {
String name
String age
}
class B {
String report
}
Import JsonBuilder in Bootstrap.groovy file
import groovy.json.JsonBuilder
An then you can try
A a = new A(name: 'ana', age: '15')
B b = new B(report: 'lorem ipsum...')
JsonBuilder jsonBuilder = new JsonBuilder()
jsonBuilder.result {
name a.name
age a.age
report b.report
}
println jsonBuilder.toString()
Output
{"result":{"name":"ana","age":"15","report":"lorem ipsum..."}}
If you want a more reusable code, then you can create a new class with public properties you want to show up in the json result.
class MyJSONResult(){
String name
String age
String report
// add more properties as needed
// constructor
MyJSONResult(A a, B b){
name = a.name
age = a.name
report = b.report
}
}
//when you need json result,
import grails.converters.JSON
def json = new MyJSONResult(a, b)
render json as JSON
The good thing about creating a new class and an object is that you can reuse it. For example if you needed to show the same result in the gsp page, you can pass the object to a view and access its properties.
def json = new MyJSONResult(a, b)
render view:'myview', model:[myVar: json]
// in gsp views
${myVar.name}
This way any business logic you used in building the json payload can be used for your api or anywhere else you may need to use them

Marshal / unmarshal domain instance to JSON in Grails

I'm trying to marshal and unmarshal a domain (e.g. OrgUnit) instance as JSON to a database field like this:
class OrgUnit{
String name
OrgUnit parent
static hasMany = [children:orgUnit]
}
class History{
String data
}
class OrgUnitService{
History marshal(OrgUnit orgUnit){
return new History([
data : (orgUnit.properties as JSON).toString()
]).save()
}
OrgUnit unmarshal(History history){
return OrgUnit.newInstance( JSON.parse(history.data))
}
}
It works fine for simple fields like name, but fields like children are empty in the unmarshaled object.
The history.data field contains children information like this:
{"name":"b","children":[{"class":"demo.OrgUnit","id":3,"children":null,"name":"c"}]}
I'm using Grails 2.2.4.
Any suggestions !?
Update
I tested it on Grails 2.4.3. It works as expected. The content of the history.data field is in both Grails versions identical. The issue is in the unmarshaling part.
The critical part is the creation of a domain instance by properties map. It is used in the default save action in controllers. It might be a security issue in controller, but I take it out of scope there.
In Grails version 2.2.4 it doesn't bind the referencies to the instance.
The workaround there is, to do it manually like this:
class HistoryService {
def grailsApplication
History marshal(OrgUnit orgUnit) {
return new History([
data: (orgUnit.properties as JSON).toString()
]).save(failOnError: true)
}
OrgUnit unmarshal(History history) {
def data = JSON.parse(history.data)
OrgUnit instance = OrgUnit.newInstance(data)
def domainClass = grailsApplication.getDomainClass(OrgUnit.class.name)
domainClass.persistentProperties.each{p->
if (p.oneToMany || p.manyToMany){
def refDataList = data."$p.name"
refDataList.each{refData->
instance."$p.name" << getDomainClass(refData.class).read(refData.id as Long)
}
}else if (p.manyToOne || p.oneToOne){
def refData = data."$p.name"
if(refData && refData.class && refData.id){ // if reference field has value null
instance."$p.name" = getDomainClass(refData.class).read(refData.id as Long)
}
}
}
return instance
}
private def getDomainClass(String domainName){
return grailsApplication.classLoader.loadClass(domainName)
}
}

Reflectively save domain class instances in Grails

The problem is as follows: I want to handle a POST request with JSON body. The body consists of an array of JSON Objects, without further nesting, i.e. simple HashMaps. All of these objects represent JSON-serialized domain classes from an Android Application, which will have their counterpart in my Grails app. I am thinking of parsing the JSON body, iterating through every element and saving each node as its corresponding domain class instance.
a) How should I save the instance? I am quite new to Grails/Groovy so please excuse any huge mistakes. Code so far is
public static Object JSONArray2Instances(String json, Class type) {
def slurper = new JsonSlurper()
def result = slurper.parseText(json)
//we only want to parse JSON Arrays
if (!(result instanceof JSONArray))
return null
result.each {
def instance = it.asType(type)
// now I need to save to domain class!
}
}
b) where do I place the corresponding code? Currently it is in /grails-app/src/groovy. Where do the tests go? (Since it is not a 'real' Grails component)
c) Is an intermediate command object more appropriate?
Your code should go in to the controller which is handling the request. Please take a look at
gson-grails plugin which has examples of how to serialize and deserialze objects and map them to domain objects. Please take a look at the grails basics where they talk about the conventions used in the grails application and the layout. There are good examples at grails site. Hope this helps
I solved my problem as follows, based on help provided by the comment from allthenutsandbolts. : (Grails-Gson plugin was not needed)
Let N2696AdminAction be the name of a Domain Class
in my controller:
class N2696AdminActionController extends RestfulController{
static responseFormats = ['json', 'xml']
def JSONHandlerService
N2696AdminActionController() {
super(N2696AdminAction)
}
#Override
#Transactional
def save(){
if (request!=null)
JSONHandlerService.instancesfromJSON(request.JSON)
}
}
then I delegate persisting to my service as follows
class JSONHandlerService {
def instancesfromJSON(Object request){
//we only want to parse JSON Arrays
if (!(request instanceof JSONArray))
return null
request.each {
def domainClass = Class.forName("${it.type}",
true, Thread.currentThread().getContextClassLoader())
def newDomainObject = domainClass.newInstance(it)
newDomainObject.save(failOnError:true, flush:true, insert: true)
}
}
}
type is a Json attribute which holds the full (package inclusive) name for my class. This way, I can save to multiple Domain Classes with the same POST request.

Groovy JsonBuilder: object properties being serialized in random order

I'm currently having a problem with the groovy JsonBuilder: the properties of the objects I am trying to serialize are coming out in seemingly random order.
Here's the objects' class:
class Game {
String title
String gameImg2
String description
}
And this is the code I've been using:
def game = new Game(title: "a game", gameImg2: "an image", description: "desc")
def json = new JsonBuilder(game)
From this I would expect the output to be:
{"title":"a game","gameImg2":"an image", "description":"desc"}
but instead, I'm getting:
{"gameImg2":"gameImg","title":"hello","description":"desc"}.
From looking at the JsonBuilder example code, it seems that order should be maintained, and indeed, it looks like the toJson method iterates over object.properties, which is a LinkedHashMap. I would have thought this would go through the properties in the order they are declared in the class.
My best guess is that this is something to do with my initialisation of the game object - could using Map syntax to assign properties somehow, but again, this seems to create a LinkedHashMap which should preserve order.
Does anyone have an inkling of what I could have done wrong or incorrectly assumed here? Any leads would be a big help!
The problem is that you are serializing an Object to JSON. The object has declared fields that are translated to Java.
In Java it is not (easily) possible to traverse fields in the order of declaration and the JsonBuilder code definitely makes no attempt to allow this behavior.
Looking at the source code for the JsonBuilder one can see that it uses a class called JsonOutput.groovy to serialize the Object you are passing it, like so:
static String toJson(object) {
if (object == null) {
"null"
} else if (object instanceof Collection ||
object.class.isArray() ||
object instanceof Iterator ||
object instanceof Enumeration) {
"[" + object.collect { toJson(it) }.join(',') + "]"
} else if (object instanceof Enum) {
'"' + object.name() + '"'
} else {
def properties = object.properties
properties.remove('class')
properties.remove('declaringClass')
properties.remove('metaClass')
toJson(properties)
}
}
As you can see the code calls the properies member of the object which returns the object members, the order of the fields depends on the JVM and not the order of declaration.
If you want to maintain order you will need to either pass in a map representing the object or build the JSON object in order manually.
The other alternative is writing your own custom builder, which is much more complicated...

ASP.NET MVC 3 controller failed to receive a json 2D array

Not sure if it is a bug or not. I followed some tutorial to post a Json 2D array to a ASP.NET MVC controller, and it works fine when the first element of the array is not empty, otherwise it fails.
Example: If my json obj is
var obj = {
ItemsArr: [[1, 2],[]] // first element is an array with a few elements
};
The controller will receive the data correctly. However if the first element of the 2D array is an empty array, like:
var obj = {
ItemsArr: [[], [1, 2]] // first element is an EMPTY array
};
the controller will receive a null.
I'm using jquery.json-2.3.min.js to convert objs to json strings.
The converted strings look fine:
{"ItemsArr":[[1,2],[]]}
{"ItemsArr":[[],[1,2]]}
The model code:
public class Model
{
public List<string[]> ItemsArr {get;set;}
}
public MyController : Controller
{
public ActionResult DoSomething( Model model )
{ ...
}
}
Have anyone met this problem? Any idea to solve it?
Thanks in advance!
===================================
Edit:
After some research, if I changed the empty array to null, it works.
{"ItemsArr":[null,[1,2]]}
I would suggest passing a array of objects and create a class with the Same properties on The server side, asp.net MVC will then automatically convert this array (on the JS end) into a list (or any other IEnumerable) that is a part of the action signature.