Groovy 1.8.0 – meet JsonBuilder!

Groovy 1.8.0 released in April brought a lot of new features to the language, one of them is native JSON support through JsonSlurper for reading JSON and JsonBuilder for writing JSON.

I recently used JsonBuilder in one of my projects and initially experienced some difficulties in understanding how it operates. My assumption was that JsonBuilder works similarly to MarkupBuilder but as I have quickly found out, it really doesn’t.

Let’s take a simple example. Assume we have a class Message that we would like to serialize to XML markup and JSON.

@groovy.transform.Canonical
class Message {
    long   id
    String sender
    String text
}
 
assert 'Message(23, me, some text)' ==
       new Message( 23, 'me', 'some text' ).toString()

Here I used Groovy 1.8.0 @Canonical annotation providing automatic toString(), equals() and hashCode() and a tuple (ordered) constructor.

Let’s serialize a number of messages to XML.

def messages = [ new Message( 23, 'me', 'some text'       ),
                 new Message( 24, 'me', 'some other text' ),
                 new Message( 25, 'me', 'same text'       )]
 
def writer = new StringWriter()
def xml    = new groovy.xml.MarkupBuilder( writer )
 
xml.messages() {
    messages.each { Message m -> message( id     : m.id,
                                          sender : m.sender,
                                          text   : m.text )}
}
 
assert writer.toString() == """
<messages>
  <message id='23' sender='me' text='some text' />
  <message id='24' sender='me' text='some other text' />
  <message id='25' sender='me' text='same text' />
</messages>""".trim()

Well, that was pretty straightforward. Let’s try to do the same with JSON.

def json = new groovy.json.JsonBuilder()
 
json.messages() {
    messages.each { Message m -> message( id     : m.id,
                                          sender : m.sender,
                                          text   : m.text )}
}
 
assert json.toString() ==
       '{"messages":{"message":{"id":25,"sender":"me","text":"same text"}}}'

Wow, where did all other messages go? Why only one last message in the list was serialized?
How about this:

json = new groovy.json.JsonBuilder()
 
json.messages() {
    message {
        id     23
        sender 'me'
        text   'some text'
    }
    message {
        id     24
        sender 'me'
        text   'some other text'
    }
}
 
assert json.toString() ==
       '{"messages":{"message":{"id":24,"sender":"me","text":"some other text"}}}'

Same story. Initially I was puzzled, but then JsonBuilder source code showed that every invocation overrides the previous content:

JsonBuilder(content = null) {
    this.content = content
}
 
def call(Map m) {
    this.content = m
    return content
}
 
def call(List l) {
    this.content = l
    return content
}
 
def call(Object... args) {
    this.content = args.toList()
    return this.content
}
 
def call(Closure c) {
    this.content = JsonDelegate.cloneDelegateAndGetContent(c)
    return content
}

As you see, one should invoke JsonBuilder exactly once, passing it a Map, List, varargs or Closure. This makes JsonBuilder very different from MarkupBuilder which can be updated as many times as needed. It could be caused by the JSON itself, whose format is stricter than free-form XML markup: something that started as a JSON map with a single Message, can not be made into array of Messages out of sudden.

The argument passed to JsonBuilder (Map, List, varargs or Closure) can also be specified in constructor so there’s no need to invoke a builder at all. You can simply initialize it with the corresponding data structure and call toString() right away. Let’s try this!

def listOfMaps = messages.collect{
                 Message m -> [ id     : m.id,
                                sender : m.sender,
                                text   : m.text ]}
 
assert new groovy.json.JsonBuilder( listOfMaps ).toString() ==
       '''[{"id":23,"sender":"me","text":"some text"},
           {"id":24,"sender":"me","text":"some other text"},
           {"id":25,"sender":"me","text":"same text"}]'''.
       readLines()*.trim().join()

Now it works :) After converting the list of messages to the list of Maps and sending them to the JsonBuilder in one go, the String generated contains all messages from the list. All code above is available in Groovy web console so you are welcome to try it out.

Btw, for viewing JSON online I recommend an excellent “JSON Visualization” application made by Chris Nielsen. “Online JSON Viewer” is another popular option, but I much prefer the first one. And for offline use “JSON Viewer” makes a good Fiddler plugin.

P.S.
If you need to read this JSON on the client side by sending, say, Ajax GET request, this can be easily done with jQuery.get():

<script type="text/javascript">
var j = jQuery;
 
j( function() {
    j.get( 'url',
           { timestamp: new Date().getTime() },
           function ( messages ){
               j.each( messages, function( index, m ) {
                   alert( "[" + m.id + "][" + m.sender + "][" + m.text + "]" );
               });
           },
           'json'
        );
});
</script>

Here I use a neat trick of a j shortcut to avoid typing jQuery too many times when using $ is not an option.

Reference: Groovy 1.8.0 – meet JsonBuilder! from our JCG partner Evgeny Goldin at the Goldin++ blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


− three = 2



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close