DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. XmlTransformer: Groovy DSL for Processing XML

XmlTransformer: Groovy DSL for Processing XML

Victor Savkin user avatar by
Victor Savkin
·
Mar. 07, 11 · Interview
Like (0)
Save
Tweet
Share
10.26K Views

Join the DZone community and get the full member experience.

Join For Free

It happens so often when you need an utility class. You create it and than you add one more to implement that “nice feature” and than more and more till you realize one day that you have a small library inside your big project.

I began with writing a small class helping me process my xml configuration files. It worked well and I added a few features to make it pre-process other XML documents as well. In my view, the result is a very simple but useful library (mostly a DSL) for processing XML. I called it XmlTransformer.

As the library is basically a domain specific language it has a semantic model underneath. XmlTransformer is the main class in the semantic model. It invokes a passed callback for each node of a tree. You can remove some nodes from the tree or change their values using that callback. This chunk of code illustrates it:

    setup:
    def t = new XmlTransformer({
        if(it.name() == 'child') it.value = 'value2'
    })

    when:
    def root = new XmlParser(false, false).parseText('''
        <root xmlns='http://myurl'>
            <child>value1</child>
        </root>
    ''')

    then:
    root.child.text() == 'value1'

    when:
    t.process root

    then:
    root.child.text() == 'value2'

One more class in the semantic model is XmlTransformerFactory containing a bunch of methods that can be used to create transformers. It just removes some boilerplate code from your callbacks. If you would like to know more about these classes just take a look at their Spock specifications.

But the interesting part of the library is a Groovy DSL on top of XmlTransformer. Using this DSL you can easily configure a bunch of transformers.

    def root = new XmlParser(false, false).parseText('''
        <root>
            <child1>value1</child1>
            <child2 attr="1">value2</child2>
            <child2 attr="2">value3</child2>
        </root>
    ''')

    def t = XmlTransformerBuilder.transformations {
        when {child2 attr: '1'}
        then {
            it.value = 'Updated value2'
        }
    }

    t.last.process root

This transformer will update the value of this node: . The variable t contains a map of transformers. You don’t specify any names here, so the only entry in this dictionary we have is t.last.

The second example is more interesting:

    def t = transformations {
        foreach = forEach {
            println it.text()
        }

        when {child1}
        updateChild1 = then {
            it.value = 'Updated value1'
        }

        when {child2 attr: '1'}
        updateChild2 = then {
            it.value = 'Updated value2'
        }

        removeChild2 = remove {
            child2 attr: '2'
        }
    }

This chuck of code defines four transformers:

* t.foreach.process root //prints the value of each tag
* t.updateChild1.process root //updates <child1>
* t.updateChild2.process root //updates <child2 attr="1">
* t.removeChild2.process root //removes <child2 attr="2">

The result xml after processing will look like this:

<root>
    <child1>Updated value1</child1>
    <child2 attr="1">Updated value2</child2>
</root>

The last example shows that you can create three types of transformers: a forEach transformer, an update transformer (using then statement) and a remove transformer.

The DSL comprises four statements: forEach, then, when and remove.

  • forEach and then statements take a closure define actions. Transformers will pass a node as the first parameter of that closure.

  • when and remove take a closure define a criteria. The criteria DSL is robust and allows you specify just tags, tags with attributes, tags containing special characters, just attributes, a list of values for an attribute, or a custom criteria.

This piece of code demonstrates how easy it is to specify complex criterias:

when {
    customTagName //true for <customTagName anyattr='vanyvalue'>
    customTagName attr1: 'value1', attr2: 'value2' //true for <customTagName attr1='value1' attr2='value2'> 
    tag('weird:tag').attrs(attr1: 'value1') //true for <weird:tag attr1='value1'> 
    attrs(attr3: 'value3') //true for any tag with attr3='value3' 
    customTagName attr4: ['value1', 'value2', 'value3'] //true for <customTagName attr4='value1'> or <customTagName attr4='value2'> or ...
    custom {it.text() == 'value1'} //true for anytag with text() == 'value1'
}

The library is extremely simple and small but I found it useful in many scenarios.

GitHub: http://github.com/avix1000/XmlTransformer

Maven:

Repository http://oss.sonatype.org/content/repositories/releases/

<dependency>
  <groupId>com.victorsavkin.grapes</groupId>
  <artifactId>XmlTransformer</artifactId>
  <version>1.0.2</version>
</dependency>

Gradle:

Repository: http://oss.sonatype.org/content/repositories/releases/
'com.victorsavkin.grapes:XmlTransformer:1.0.2'

 

From http://victorsavkin.com/post/3660212172/xmltransformer-groovy-dsl-for-processing-xml

Domain-Specific Language XML Processing Groovy (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • DZone's Article Submission Guidelines
  • The Path From APIs to Containers
  • How to Submit a Post to DZone
  • HTTP vs Messaging for Microservices Communications

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: