How to exclude null properties in json using groovy - json

Using JSONOutput.toJSON to convert objects to json representation in groovy, is there a way to exclude null values to compact the JSON ?
Understand it can be done using jackson, but currently using groovy JSonOutput.
Current Implementation :
Java Domain
class User {
String firstName;
String lastName;
}
Groovy Script
def user = new User()
user.firstName="abc"
def mapUser = ["userkey",user]
JsonOutput.toJson(mapUser)
Actual Output
{"userkey":{"firstName":abc,"lastName":null}}
Expected Output
{"userkey":{"firstName":abc}}

You also could use JsonGenerator, but it appeared since 2.5.0:
def user = new User()
user.firstName="abc"
def mapUser = ["userkey",user]
def generator = new JsonGenerator.Options()
.excludeNulls()
.build()
generator.toJson(mapUser)
and you'll get wat you want

You could manually create a map from the objects' properties and serialize that:
def "exclude null values"() {
given:
def user = new User(firstName: 'abc')
def mapUser = [ "userkey":
user.properties
.findAll { it.key != 'class' && it.value }
]
expect:
JsonOutput.toJson(mapUser) == /{"userkey":{"firstName":"abc"}}/
}
(spock test)

Related

How to construct empty Array list in Json using Rest Assured

I am serializing the Json from POJO class. I want to send empty array in Request but when i pass null or empty value to the properties in pojo class, i am getting [""] in array list instead of []
Expected: "Key": []
But I am getting: "Key":[""]
My pojo:
public class Sample {
private List<String> Key=null;
}
Test data builder class:
Sample s = new Sample();
s.Key = ""; // Even if i give null here, i am getting null at the json payload.
Pls suggest ways to construct an empty array for Key as below.
{
"Key": []
}
Just initialize new ArrayList, like this.
Sample s = new Sample();
s.Key = new ArrayList<>();

Check if the JSON from the Response is NULL or not

I am trying to check if the response from the API GET method is Null. The response is like
{
"#odata.context": "https://dev.com/data/$metadata#Customers",
"value": []
}
I need to check if the value array is null or not and do the necessary steps below is what I tried
public class deserializeData
{
public String #odata_context {get;set;} // in json: #odata.context
public List<Value> value {get;set;}
}
public class getDataFromExternalSystem{
public string getDataFrom(){
.......
Http http1 = new Http();
HttpRequest req1 = new HttpRequest();
req1.setEndpoint(endPoint);
req1.setMethod('GET');
req1.setHeader('Authorization','Bearer '+atoken);
HttpResponse res1 = http1.send(req1);
System.debug('Response Body=========' + res1.getBody());
deserializeData dsData = (deserializeData)JSON.deserialize(res1.getbody(),deserializeData.class);
if(dsData.value.size = null) {
......
}
else{
......
}}
But I get below error like
#odata_context is not a legal identifier in Apex. # is used to introduce annotations.
If you're using JSON2Apex, which appears to be the case, you'll need to change the name of the property to something legal for Apex (like odata_context), and make the corresponding change in the parser method. E.g., where JSON2Apex generates
if (text == '#odata.context') {
#odata_context = parser.getText();
you'll need to replace that identifier with the new one you choose.

Grails rendering JSON

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

xtend method implementation with Annotations for the parameter

How can I implement this method with Xtend?
private void myListenerMethod(#Observes(notifyObserver=IF_EXISTS) #MyAnnotation Boolean value)
{
... Do somehting ...
}
I have here an example for a normal method implementation:
var method_myListenerMethod = toMethod("myListenerMethod", newTypeRef(void), [
'''
... Do something ...
'''
])
method_myListenerMethod.parameters += toParameter("value",newTypeRef('''javax.enterprise.event.Observes'''))
members += method_doSaveOperation
I'm not sure what your methods toMethod and toParameter mean, but I would do generation of your method declaration as follows:
private def addMethod(MutableFieldDeclaration field, extension TransformationContext context) {
field.declaringType.addMethod("myListenerMethod") [ methodDecl |
methodDecl.returnType = newTypeReference(typeof(void))
val param = methodDecl.addParameter("value", newTypeReference(typeof(Boolean)))
val observes = param.addAnnotation(context.findTypeGlobally("javax.enterprise.event.Observes"))
val reception = context.findTypeGlobally("javax.enterprise.event.Reception") as EnumerationTypeDeclaration
observes.setEnumValue("notifyObserver", reception.findDeclaredValue("IF_EXISTS"))
param.addAnnotation(context.findTypeGlobally("MyAnnotation"))
]
}
This requires at least Xtend 2.5.0 (see bug #403789)

Grails JSONBuilder

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.