Search

Dark theme | Light theme
Showing posts with label GroovyGoodness:JSON. Show all posts
Showing posts with label GroovyGoodness:JSON. Show all posts

June 14, 2018

Groovy Goodness: Customizing JSON Output

Groovy 2.5.0 adds the possibility to customize JSON output via a JsonGenerator instance. The easiest way to turn an object into a JSON string value is via JsonOutput.toJson. This method uses a default JsonGenerator with sensible defaults for JSON output. But we can customize this generator and create JSON output using the custom generator. To create a custom generator we use a builder accessible via JsonGenerator.Options. Via a fluent API we can for example ignore fields with null values in the output, change the date format for dates and ignore fields by their name or type of the value. And we can add a custom converter for types via either an implementation of the conversion as Closure or implementation of the JsonGenerator.Converter interface. To get the JSON string we simple invoke the toJson method of our generator.

In the following example Groovy code we have a Map with data and we want to convert it to JSON. First we use the default generator and then we create our own to customize the JSON output:

// Sample class to be used in JSON.
@groovy.transform.TupleConstructor
class Student { 
    String firstName, lastName
}

def data = 
    [student: new Student('Hubert', 'Klein Ikkink'),
     dateOfBirth: Date.parse('yyyyMMdd', '19730709'),
     website: 'https://www.mrhaki.com'.toURL(),
     password: 'IamSecret',
     awake: Optional.empty(),
     married: Optional.of(true), 
     location: null,
     currency: '\u20AC' /* Unicode EURO */]
     

import groovy.json.JsonGenerator
import groovy.json.JsonGenerator.Converter
        
// Default JSON generator. This generator is used by
// Groovy to create JSON if we don't specify our own. 
// For this example we define the default generator 
// explicitly to see the default output.       
def jsonDefaultOutput = new JsonGenerator.Options().build()
        
// Use generator to create JSON string.
def jsonDefaultResult = jsonDefaultOutput.toJson(data) // Or use JsonOutput.toJson(data)

assert jsonDefaultResult == '{"student":{"firstName":"Hubert","lastName":"Klein Ikkink"},' + 
    '"dateOfBirth":"1973-07-08T23:00:00+0000","website":"https://www.mrhaki.com","password":"IamSecret",' + 
    '"awake":{"present":false},"married":{"present":true},"location":null,"currency":"\\u20ac"}'


// Define custom rules for JSON that will be generated.
def jsonOutput = 
    new JsonGenerator.Options()
        .excludeNulls()  // Do not include fields with value null.
        .dateFormat('EEEE dd-MM-yyyy', new Locale('nl', 'NL')) // Set format for dates.
        .timezone('Europe/Amsterdam') // Set timezone to be used for formatting dates.
        .excludeFieldsByName('password')  // Exclude fields with given name(s). 
        .excludeFieldsByType(URL)  // Exclude fields of given type(s).
        .disableUnicodeEscaping()  // Do not escape UNICODE.
        .addConverter(Optional) { value -> value.orElse('UNKNOWN') } // Custom converter for given type defined as Closure.
        .addConverter(new Converter() {  // Custom converter implemented via Converter interface.
        
            /**
             * Indicate which type this converter can handle.
             */
            boolean handles(Class<?> type) { 
                return Student.isAssignableFrom(type)
            }
            
            /**
             * Logic to convert Student object.
             */
            Object convert(Object student, String key) {
                "$student.firstName $student.lastName"
            }
            
        })
        .build()  // Create the converter instance.

// Use generator to create JSON from Map data structure.
def jsonResult = jsonOutput.toJson(data)

assert jsonResult == '{"student":"Hubert Klein Ikkink",' + 
    '"dateOfBirth":"maandag 09-07-1973",' + 
    '"awake":"UNKNOWN","married":true,"currency":"€"}'

The JsonBuilder and StreamingJsonBuilder classes now also support the use of a JsonGenerator instance. The generator is used when the JSON output needs to be created. The internal data structure of the builder is not altered by using a custom generator.

In the following example we use the custom generator of the previous example and apply it with a JsonBuilder and StreamingJsonBuilder instance:

import groovy.json.JsonBuilder

// We can use a generator instance as constructor argument
// for JsonBuilder. The generator is used when we create the
// JSON string. It will not effecct the internal JSON data structure.
def jsonBuilder = new JsonBuilder(jsonOutput)
jsonBuilder {
    student new Student('Hubert', 'Klein Ikkink')
    dateOfBirth Date.parse('yyyyMMdd', '19730709')
    website 'https://www.mrhaki.com'.toURL()
    password 'IamSecret'
    awake Optional.empty()
    married Optional.of(true)
    location null
    currency  '\u20AC' 
}

def jsonBuilderResult = jsonBuilder.toString()

assert jsonBuilderResult == '{"student":"Hubert Klein Ikkink",' + 
    '"dateOfBirth":"maandag 09-07-1973",' + 
    '"awake":"UNKNOWN","married":true,"currency":"€"}'

// The internal structure is unaffected by the generator.
assert jsonBuilder.content.password == 'IamSecret'
assert jsonBuilder.content.website.host == 'www.mrhaki.com'


import groovy.json.StreamingJsonBuilder

new StringWriter().withWriter { output -> 

    // As with JsonBuilder we can provide a custom generator via
    // the constructor for StreamingJsonBuilder.
    def jsonStreamingBuilder = new StreamingJsonBuilder(output, jsonOutput)
    jsonStreamingBuilder {
        student new Student('Hubert', 'Klein Ikkink')
        dateOfBirth Date.parse('yyyyMMdd', '19730709')
        website 'https://www.mrhaki.com'.toURL()
        password 'IamSecret'
        awake Optional.empty()
        married Optional.of(true)
        location null
        currency  '\u20AC' 
    }

    def jsonStreamingBuilderResult = output.toString()
    
    assert jsonStreamingBuilderResult == '{"student":"Hubert Klein Ikkink",' + 
        '"dateOfBirth":"maandag 09-07-1973",' + 
        '"awake":"UNKNOWN","married":true,"currency":"€"}'
}

Written with Groovy 2.5.0.

February 17, 2017

Groovy Goodness: Creating Root JSON Array With JsonBuilder

To create JSON output with Groovy is easy using JsonBuilder and StreamingJsonBuilder. In the samples mentioned in the links we create a JSON object with a key and values. But what if we want to create JSON with a root JSON array using JsonBuilder or StreamingJsonBuilder? It turns out to be very simple by passing a list of values using the constructor or using the implicit method call.

In the following example we use JsonBuilder to create a root JSON array:

import groovy.json.JsonBuilder

// Example class.
@groovy.transform.Immutable
class Villain { 
    String name 
}

// A list of Villain objects that needs to transformed
// to a JSON array.
def list = ['The Joker', 'Penguin', 'Catwoman', 'Harley Quinn'].collect { name -> new Villain(name) }

// We create a new JsonBuilder and 
// use the list of Villain objects
// as argument for the constructor
// to create a root JSON array.
def json1 = new JsonBuilder(list)

assert json1.toString() == '[{"name":"The Joker"},{"name":"Penguin"},{"name":"Catwoman"},{"name":"Harley Quinn"}]'


// Here we use the no-argument constructor
// to create a JsonBuilder.
// Then we use the instance implicit
// method call with the list of Villain
// objects as arguments
def json2 = new JsonBuilder()
json2(list)

assert json2.toString() == '[{"name":"The Joker"},{"name":"Penguin"},{"name":"Catwoman"},{"name":"Harley Quinn"}]'

In the next example we use StreamingJsonBuilder to create a root JSON array:

import groovy.json.StreamingJsonBuilder

// Example class.
@groovy.transform.Immutable
class Villain { 
    String name 
}

// A list of Villain objects that needs to transformed
// to a JSON array.
def list = ['The Joker', 'Penguin', 'Catwoman', 'Harley Quinn'].collect { name -> new Villain(name) }

// Here we use the no-argument constructor
// to create a JsonBuilder.
// Then we use the instance implicit
// method call with the list of Villain
// objects as arguments
def json = new StringWriter()
def jsonBuilder = new StreamingJsonBuilder(json)
jsonBuilder(list)

assert json.toString() == '[{"name":"The Joker"},{"name":"Penguin"},{"name":"Catwoman"},{"name":"Harley Quinn"}]'

There is also an implicit method call that takes an extra Closure argument. For each element in the list the Closure is invoked with the element as argument. This way we can customize the root JSON array output using the properties of the object that is in the list. In the following example we use both the JsonBuilder and StreamingJsonBuilder classes to transform the elements in the list:

import groovy.json.JsonBuilder
import groovy.json.StreamingJsonBuilder

// Example class.
@groovy.transform.Immutable
class Villain { 
    String name 
}

// A list of Villain objects that needs to transformed
// to a JSON array.
def list = ['The Joker', 'Penguin', 'Catwoman', 'Harley Quinn'].collect { name -> new Villain(name) }

// We create a new JsonBuilder and 
// then we use the instance implicit
// method call with the list of Villain
// objects as arguments and closure
// to transform each Villain object
// in the root JSON array.
def json1 = new JsonBuilder() 
json1(list) { Villain villain ->
    name villain.name
    initials villain.name.split(' ').collect { it[0].toUpperCase() }.join()
}

assert json1.toString() == '[{"name":"The Joker","initials":"TJ"},{"name":"Penguin","initials":"P"},{"name":"Catwoman","initials":"C"},{"name":"Harley Quinn","initials":"HQ"}]'


// We can use the same implicit
// method call for a list and 
// closure to transform each element
// to a root JSON array.
def json = new StringWriter()
def json2 = new StreamingJsonBuilder(json)
json2(list) { Villain villain -> 
    name villain.name
    initials villain.name.split(' ').collect { it[0].toUpperCase() }.join()
}

assert json.toString() == '[{"name":"The Joker","initials":"TJ"},{"name":"Penguin","initials":"P"},{"name":"Catwoman","initials":"C"},{"name":"Harley Quinn","initials":"HQ"}]'

Written with Groovy 2.4.8

August 5, 2014

Groovy Goodness: Relax... Groovy Will Parse Your Wicked JSON

Since Groovy 2.3 the JSON parser has improved and is really a performant parser for JSON payload. JSON must be formatted in a strict way, for example all keys must be enclosed in double quotes. But what if we have JSON which not applies to the strict specification? For example a configuration file written in JSON and we want to add comments to this file. Or use single quotes for keys, or just leave all the quotes out. We can now specify the type JsonParseType.LAX for our JSON parser and we can now parse JSON which doesn't apply to the strict specification.

import groovy.json.*
import static groovy.json.JsonParserType.LAX as RELAX

def jsonText = '''
{
    message: {
        
        /* Set header of message */
        header: {
            "from": 'mrhaki',
            'to': ["Groovy Users", "Java Users"]
        }
        
        # Include a little body for the message.
        'body': "Check out Groovy's gr8 JSON support."
    }
}       
'''

def json = new JsonSlurper().setType(RELAX).parseText(jsonText)

def header = json.message.header
assert header.from == 'mrhaki'
assert header.to[0] == 'Groovy Users'
assert header.to[1] == 'Java Users'
assert json.message.body == "Check out Groovy's gr8 JSON support."

Code written with Groovy 2.3.6.

January 23, 2012

Groovy Goodness: Solve Naming Conflicts with Builders

With Groovy we can use for example the MarkupBuilder or JSONBuilder to create XML or JSON content. The builders are a very elegant way to create the content. Most builders in Groovy use the invokeMethod and getProperty and setProperty methods to dynamically build the contents. But this also means that if we have builder node names that are the same as method or property names in the local context of our code running the builder, that we have a naming conflict. Let's see this with a simple sample:

import groovy.xml.*

def body = []

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.message {
    body(contentType: 'plain') {
        text 'Simple message'
    }
}

def contents = writer.toString()
println contents

When we run this code we get an error message:

groovy.lang.MissingMethodException: No signature of method: java.util.ArrayList.call() is applicable for argument types: (java.util.LinkedHashMap, xmlmessage$_run_closure1_closure2) values: [[contentType:plain], xmlmessage$_run_closure1_closure2@50502819]
Possible solutions: tail(), wait(), last(), any(), max(), clear()
 at xmlmessage$_run_closure1.doCall(xmlmessage.groovy:7)
 at xmlmessage$_run_closure1.doCall(xmlmessage.groovy)
 at xmlmessage.run(xmlmessage.groovy:6)

Groovy tries to use the ArrayList class of our books variable to execute a call method with two parameters of type LinkedHashMap and closure. So our local variable is found by the builder and the builder tries to use this variable, which results in the shown error.

The following sample shows what happens if we have a local method with the same name as a node in the builder:

import groovy.xml.*

def body(value) {
    println "body contents is $value"
}

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.message {
    body {
        text 'Simple message'
    }
}

def contents = writer.toString()
println contents

We get the following output if we run this script:

body contents is 
<message>
  <text>Simple message</text>
</message>

Our method with the name body has the same signature and name as the body node in the builder. Our local method is invoked and we see the output of the println statement.

To solve this naming conflict we can change the name of our local variable or method. But this is not always possible or desired. Imagine the method or variable is dynamically added to our class than we cannot change the name. But we can also change our builder syntax slightly to get what we want.

To force our builder to use the builder's code to create the contents we can prepend the node name with delegate. Delegate is the closure context of our builder. This way our builder will not use any already defined variable or method names to create the content.

import groovy.xml.*

def body = []

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
builder.message {
    delegate.body(contentType: 'plain') {
        text 'Simple message'
    }
}

def contents = writer.toString()
println contents

When we run the script we get the following output:

<message>
  <body contentType='plain'>
    <text>Simple message</text>
  </body>
</message>

And that is the output we want.

If we are using Grails and use the render method to create XML or JSON we must be aware that methods are also dynamically available in a controller. For example the message method from the Grails tag libraries is a method in a controller:

package builder.naming

class SampleController {

    def index() {
        render(contenType: 'text/xml') {
            message {
                content 'Contents'
            }
        }
    }
}

If we invoke this controller we get the following XML output:

<content>Contents</content>

But if we change the code to:

package builder.naming

class SampleController {

    def index() {
        render(contenType: 'text/xml') {
            delegate.message {
                content 'Contents'
            }
        }
    }
}

We get the following output:

<message><content>Contents</content></message>

September 21, 2011

Groovy Goodness: Streaming JSON with StreamingJsonBuilder

Since Groovy 1.8 we can use JSONBuilder to create JSON data structures. With Groovy 1.8.1 we have a variant of JsonBuilder that will not create a data structure in memory, but will stream directly to a writer the JSON structure: StreamingJsonBuilder. This is useful in situations where we don't have to change the structure and need a memory efficient way to write JSON.

import groovy.json.*

def jsonWriter = new StringWriter()
def jsonBuilder = new StreamingJsonBuilder(jsonWriter)
jsonBuilder.message {
    header {
        from(author: 'mrhaki')  
        to 'Groovy Users', 'Java Users'
    }
    body "Check out Groovy's gr8 JSON support."
}
def json = jsonWriter.toString()
assert json == '{"message":{"header":{"from":{"author":"mrhaki"},"to":["Groovy Users","Java Users"]},"body":"Check out Groovy\'s gr8 JSON support."}}'

def prettyJson = JsonOutput.prettyPrint(json)
assert prettyJson == '''{
    "message": {
        "header": {
            "from": {
                "author": "mrhaki"
            },
            "to": [
                "Groovy Users",
                "Java Users"
            ]
        },
        "body": "Check out Groovy's gr8 JSON support."
    }
}'''


new StringWriter().withWriter { sw ->
    def builder = new StreamingJsonBuilder(sw)

    // Without root element.
    builder name: 'Groovy', supports: 'JSON'

    assert sw.toString() == '{"name":"Groovy","supports":"JSON"}'
}

new StringWriter().with { sw ->
    def builder = new StreamingJsonBuilder(sw)

    // Combine named parameters and closures.
    builder.user(name: 'mrhaki') {
        active true
    }
    
    assert sw.toString() == '{"user":{"name":"mrhaki","active":true}}'    
}

April 27, 2011

Groovy Goodness: Build JSON with JsonBuilder and Pretty Print JSON Text

Groovy 1.8 adds JSON support. We can build a JSON data structure with the JsonBuilder class. This class functions just like other builder classes. We define a hiearchy with values and this is converted to JSON output when we view the String value. We notice the syntax is the same as for a MarkupBuilder.

import groovy.json.*

def json = new JsonBuilder()

json.message {
    header {
        from('mrhaki')  // parenthesis are optional
        to 'Groovy Users', 'Java Users'
    }
    body "Check out Groovy's gr8 JSON support."
}

assert json.toString() == '{"message":{"header":{"from":"mrhaki","to":["Groovy Users","Java Users"]},"body":"Check out Groovy\'s gr8 JSON support."}}'

// We can even pretty print the JSON output
def prettyJson = JsonOutput.prettyPrint(json.toString())
assert prettyJson == '''{
    "message": {
        "header": {
            "from": "mrhaki",
            "to": [
                "Groovy Users",
                "Java Users"
            ]
        },
        "body": "Check out Groovy's gr8 JSON support."
    }
}'''

Groovy Goodness: Parse JSON with JsonSlurper

With Groovy 1.8 we can parse JSON text with the JsonSlurper class. We only have to feed the text to the parseText() method and we can the values are mapped to Maps and Lists. And getting the content then is very easy:

import groovy.json.*

def jsonText = '''
{
    "message": {
        "header": {
            "from": "mrhaki",
            "to": ["Groovy Users", "Java Users"]
        },
        "body": "Check out Groovy's gr8 JSON support."
    }
}       
'''

def json = new JsonSlurper().parseText(jsonText)

def header = json.message.header
assert header.from == 'mrhaki'
assert header.to[0] == 'Groovy Users'
assert header.to[1] == 'Java Users'
assert json.message.body == "Check out Groovy's gr8 JSON support."