XML rendering in Grails

Notes taken when researching how to render efficiently a possibly large object tree resulting in complex XML.

Disclaimer: tested with Grails 1.3.7, but it should be valid in more recent framework versions

Convert object to XML string using XML converter

def ping = {
    def result = new Result(response: 'OK')
    def xml = new XML(result)
    render text: xml, contentType: 'text/xml', encoding: 'UTF-8'
}
  • adds explicit <class>…</class> element to the output
  • double transformation – object -> XML string -> ServletResponse output stream
  • relies on poorly documented Grails contracts – here be dragons

Variation – deep conversion

XML.use('deep') {
    def xml = new XML(result)
    render text: xml, contentType: 'text/xml', encoding: 'UTF-8'
}

Variation – customize conversion with a custom marshaller

class ResultMarshaller implements ObjectMarshaller {
    @Override
    boolean supports(object) {
        object instanceof Result
    }

    @Override
    void marshalObject(object, Converter converter) {
        // converter is an instance of XML
        converter.build {
            response object.response
        }
    }
}

class ResultRootElementCustomizingMarshaller extends ResultMarshaller implements NameAwareMarshaller {
    @Override
    String getElementName(o) {
        'wynik'
    }
}

Unit tests

def 'ping, no marshaller'() {
    when:
    controller.ping()

    then:
    XmlAsserts.xmlsEqual controller.response.contentAsString, """<result>
    <class>${Result.name}</class>
    <response>OK</response>
</result>"""
}

def 'ping, marshaller'() {
    given:
    XML.registerObjectMarshaller new ResultMarshaller()

    when:
    controller.ping()

    then:
    XmlAsserts.xmlsEqual controller.response.contentAsString, '<result><response>OK</response></result>'
}

def 'ping, marshaller customizing root element name'() {
    given:
    XML.registerObjectMarshaller new ResultRootElementCustomizingMarshaller()

    when:
    controller.ping()

    then:
    XmlAsserts.xmlsEqual controller.response.contentAsString, '<wynik><response>OK</response></wynik>'
}

Render object to the response using XML converter

def pingConverterRendersToResponse = {
    def xml = new XML(new Result(response: 'OK'))
    xml.render response
}

Unit tests

def 'ping, converter renders to response'() {
    when:
    controller.pingConverterRendersToResponse()

    then:
    XmlAsserts.xmlsEqual controller.response.contentAsString, """<result>
<class>${Result.name}</class>
<response>OK</response>
</result>"""
}

Convert object to XML string using StreamingMarkupBuilder

def pingViaBuilder = {
    def res = new Result(response: 'OK')
    render contentType: 'text/xml', encoding: 'UTF-8', {
        result {
            response res.response
        }
    }
}
  • feature provided by render method
  • direct write into ServletResponse writer
  • could lead to repeated rendering code

Unit tests

def 'ping, direct render'() {
    when:
    controller.pingViaBuilder()

    then:
    XmlAsserts.xmlsEqual controller.response.contentAsString, '<result><response>OK</response></result>'
}

Common objects/helper methods used in examples

class Result {
    String response
}

class XmlAsserts {
    static void xmlsEqual(String actual, String expected) {
        XMLUnit.ignoreWhitespace = true
        def diff = new Diff(actual, expected)
        assert diff.similar(), "${diff.toString()}\nActual XML: $actual\n\nExpected XML: $expected"
    }
}

Instant Markdown book released

I got an opportunity to do the technical review of Instant Markdown book written by Arturo Herrero.
Instant Markdown cover

The book is divided in three parts. First part is about Markdown to HTML processor installation. You can skip it if you won’t generate HTML from manually.

Second part contains the complete language reference in case you want have it at your fingertips. What I miss from this part are examples with a side-by-side comparison of Markdown markup – generated HTML – rendered document, like in many desktop Markdown editors. You would get better grasp of the relationship between Markdown syntax, resulting HTML tags and visual representation of the document.

I enjoyed the third part the most. It tells you about cool applications of Markdown. I already knew GitHub, its comments and readme pages. But there are other useful online services when you can use Markdown to write and publish blog posts and documents.

In short, it is a quick but entertaining reading, like any book from the Instant series. On less that 50 pages, you get a complete Markdown syntax reference and some useful hints.

Find all dirty Grails tests

Aside

cut + grep + awk + sed in action; prerequisite: Git repository:

git status -s | \
cut -c4- | \
grep "$test_type.*[Test|Spec]" | \
awk 'BEGIN { FS = "/" } ; { print ($(NF)) }' | \
sed 's/\.groovy//' | tr '\n' ' '

Hamcrest matchers in Spock

Working with Spock we forget about a hidden gem – Hamcrest matchers. They are assertions turned into method objects. A matcher is usually parametrized with an expected value and is executed latel on the actual one.

When can we use it? For example in a table-driven specification with expected values in the table.

An expected value can be one of the following:

  • exact value
  • ‘not available’ text
  • any number

How can we express these assertions without Hamcrest?

then:
if (exactValue) {
    assert field == exactValue
} else if (notAvailable) {
    assert field == messageSource.getMessage('na', null, LocaleContextHolder.locale)
} else if (isNumber) {
    assert field.number
} else {
    assert false, 'Wrong test configuration'
}

where:
exactValue | notAvailable | isNumber
'12345'    | null         | null
null       | true         | null
null       | null         | true

Bufff…

  • there is a lot of boilerplate code
  • the specification is misleading – what does it mean that expected exactValue is null? Is it really null? Or simply this condition should not be checked?

How can we refactor this code using Hamcrest matchers?

then:
that field, verifiedBy

where:
verifiedBy << [
    org.hamcrest.CoreMatchers.equalTo('12345')
    notAvailable(),
    isNumber(),
]

...

def notAvailable() {
    org.hamcrest.CoreMatchers.equalTo messageSource.getMessage('na', null, LocaleContextHolder.locale)
}

def isNumber() {
    [
        matches: { actual -> actual.number },
        describeTo: { Description description -> 'should be a number' }
    ] as BaseMatcher
}

Result – shorter and more expressive feature method code.

You can find the full source code with mocked messageSource in this gist. For more information about Hamcrest matchers, check Luke Daley’s article.