Jason
Jason

Reputation: 12283

How to exclude null properties from marshalling with Grails XML marshaller

When marshalling to XML in Grails 2.3.5, I want null properties to be excluded from the XML.

Currently code like this

if ( myObj.someProp == null ) log.info("yeap, it's null")
render myObj as XML

will output "yeap, it's null" to the log and then render XML that looks like

<myObj>
    <someProp />
</myObj>

but what I want is

<myObj>
</myObj>

Or simply (if no other properties are present)

<myObj />

Can Grails be configured to exclude null properties from marshalling?

Also, using

def xml = myObj as XML
if ( myObj.someProp == null ) {
    xml.excludes = ["someProp"]
}
render xml

won't work in all cases because sometimes it's a list of results where the items in the list may have null properties.

UPDATE

I tried @dmahapatro's suggestion by adding the following code to BootStrap.groovy

    XML.registerObjectMarshaller(MyObj) {
        it.properties.findAll { it.value != null }
    }

with the appropriate import statements.

The results are XML that is incorrect:

<myObj>
    <entry key="someProp">non-null value</entry>
</myObj>

Now the properties are no longer the names of tags. Instead, a generic "entry" tag is used, because Grails is seeing a Map instead of a domain object.

Upvotes: 1

Views: 1891

Answers (2)

Jason
Jason

Reputation: 12283

It is not possible to exclude tags for null values from the output using the built-in converters and marshallers. Having read the Grails source code there are two relevant classes that control this behavior, neither of which is configurable to behave differently.

The first is the marshaller class DomainClassMarshaller in grails-plugin-converters/src/main/groovy/org/codehaus/groovy/grails/web/converters/marshaller/xml/ and the relevant lines are

xml.startNode(propertyName);
if (!property.isAssociation()) {
    // Write non-relation property
    Object val = beanWrapper.getPropertyValue(propertyName);
    xml.convertAnother(val);
}

This starts the node for the property (regardless of its value) then calls xml.convertAnother on the value.

The second relevant line of code is the convertAnother method in XML.java of grails-plugin-converters/src/main/groovy/grails

if (o == null) {
    // noop
}

Which does nothing if the value is null (leaving the tag empty) and references no configuration options to behave differently.

Therefore, the only way to get the desired behavior is to implement it from scratch and register the custom marshaller/converter as @dmahapatro suggested.

Since the OP asked how to do this with the Grails XML Marshaller, the correct answer is simply: it cannot be done.

Upvotes: 1

dmahapatro
dmahapatro

Reputation: 50245

You can use registerObjectMarshaller to modify that behavior.

//Bootstrap.groovy
import grails.converters.XML
import my.package.MyObj

XML.registerObjectMarshaller(MyObj) {
    it.properties.findAll { it.value != null }
}

UPDATE
In order to avoid the entry tags because of map representation of domain class, the object marshaller can be easily transformed to get expected result as below:

XML.registerObjectMarshaller(Author) {obj, xml ->
    xml.attribute 'id', "$obj.id"
    xml.build {
        obj.properties.each {
            if(it.value != null) {
                "$it.key" it.value
            }
        }
    }
}

Upvotes: 2

Related Questions