Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Andrey Adamovich - Groovy 2 Cookbook - 2013.pdf
Скачиваний:
44
Добавлен:
19.03.2016
Размер:
26.28 Mб
Скачать

Chapter 6

See also

ff Parsing JSON messages with JsonSlurper

ff http://groovy.codehaus.org/Operators

ff http://docs.codehaus.org/display/GROOVY/Groovy+Truth

ff http://json-schema.org/

ff https://github.com/fge/json-schema-validator

ff https://github.com/EqualExperts/json-schema-validator

Converting JSON message to XML

JSON and XML are the de facto data interchange standard formats used by industry applications. The two formats share many similarities, but have different goals in their design. JSON is designed to be a data exchange language, which is human readable and easy for computers to parse and use. XML also shares the readability goal, but suffers from a higher degree of verbosity and complexity.

Nevertheless, the two formats are here to stay, and a case for converting from one format to the other is recurring in the IT industry.

This recipe shows how to convert a JSON document into XML using a Groovy only solution as well as introducing a less "groovier" solution in the There's more... section.

Getting ready

The JSON document to convert is the ui.json file that we already encountered in the Parsing JSON messages with JsonSlurper recipe.

How to do it...

The conversion is based on groovy.xml.MarkupBuilder, which allows full control on the final output but makes the code more verbose because we have to explicitly specify all the transformation rules.

1.In a script copy the following code, making sure that the ui.json file is in the same folder as the Groovy script:

import groovy.json.JsonSlurper import groovy.xml.MarkupBuilder

def reader = new FileReader('ui.json')

211

www.it-ebooks.info

Working with JSON in Groovy

def ui = new JsonSlurper().parse(reader)

def writer = new StringWriter()

def xml = new MarkupBuilder(writer)

2.Now you can use the builder to pass data from the parsed message:

xml.items {

ui.items.each { widget -> item(type: widget.type, height: widget.height, width: widget.width) {

axes {

widget.axes.each { widgetAxis -> axis(type: widgetAxis.type, name: widgetAxis.title)

}

}

}

}

}

println writer.toString()

3.The output of the code is as follows:

<items>

<item type='chart' height='270' width='319'> <axes>

<axis type='Time' name='Time' />

<axis type='Numeric' name='Profit in EUR' /> </axes>

</item>

</items>

How it works...

The code in step 2 is really a testament to the sheer efficiency of builders (see the Defining data structures as code in Groovy recipe in Chapter 3, Using Groovy Language Features). The variable xml is used to construct the XML element names and attributes that are extracted, via dynamic methods, from the ui variable, created by the MarkupBuilder. More information on MarkupBuilder can be found in the Constructing XML content recipe in Chapter 5, Working with XML in Groovy.

212

www.it-ebooks.info

Chapter 6

To avoid confusing the Groovy compiler, when we are inside the iterator closures (for example, widget.axes.each { widgetAxis -> ...) we have to use variable names that are different from the XML element names (for example, axis(type: widgetAxis.type

...).

There's more...

The second approach mentioned in this recipe's introduction is to use a third-party library, such as JSON-lib to perform the transformation with little coding. The following code makes use of the XMLSerializer class to transform the JSON data to XML.

@Grapes([ @Grab(group='net.sf.json-lib',

module='json-lib', version='2.3', classifier='jdk15'),

@Grab('xom:xom:1.2.5')

])

import net.sf.json.JSONObject import net.sf.json.xml.XMLSerializer

def object = JSONObject.fromObject(new File('ui.json').text)

println new XMLSerializer().write(object)

Here we trade lines of code for lack of control above the produced XML, that is not quite as readable as the Groovy only example:

<?xml version="1.0" encoding="UTF-8"?> <o>

<items class="array"> <e class="object">

<animate type="boolean">true</animate> <axes class="array">

<e class="object"> <fields class="array">

<e type="string">x</e> </fields>

<position type="string">left</position> <title type="string">Time</title> <type type="string">Time</type>

</e>

...

213

www.it-ebooks.info

Working with JSON in Groovy

</axes>

...

</e>

...

</items>

</o>

The output can still be tweaked by setting different options of the XmlSerializer class, but you would not be able to fully control the complete structure of the output. The technique outlined here may be established for very complex JSON documents, which would require a non-trivial amount of code to perform the conversion to XML. An additional approach could be to apply XSLT transformation to the result produced by the XmlSerializer, but describing the process goes beyond the scope of this recipe.

See also

ff Constructing JSON messages with JsonBuilder

ff The Constructing XML content recipe in Chapter 5, Working with XML in Groovy ff http://json-lib.sourceforge.net/

ff http://json-lib.sourceforge.net/apidocs/jdk15/net/sf/json/xml/ XMLSerializer.html

ff http://groovy.codehaus.org/gapi/groovy/json/JsonBuilder.html ff http://groovy.codehaus.org/api/groovy/xml/MarkupBuilder.html

Converting JSON message to Groovy Bean

The power of the Java/Groovy type system, reflection API, and other goodies may be very handy if you need to make more type-safe code.

JSON by definition is not doing any type checking, but it is possible to map the JSON data to the Java/Groovy objects to present data inside your application and get access to type information. And that's what we will demonstrate in this recipe.

Getting ready

First of all let's define a Groovy Bean (POGO) class, which holds data representing some vehicle information:

package org.groovy.cookbook

import groovy.transform.ToString

@ToString

214

www.it-ebooks.info

Chapter 6

class Vehicle {

static enum FuelType { DIESEL, PETROL, GAS, ELECTRIC } static enum TransmissionType { MANUAL, AUTOMATIC }

@ToString

static class Transmission { long gears TransmissionType type

}

String brand

String model

FuelType fuel

Long releaseYear

Transmission transmission

}

As you can notice it's nothing, but a set of fields of simple types, enumerations, and a nested class, which again consists of simple type fields. In order for that class to be available for the conversion script, let's place it into a vehicle.groovy file and compile it with the groovyc command:

groovyc vehicle.groovy

The JSON document to which we want to map the class is the one defined in the recipe, Validating JSON messages. Create a vehicle.json file in the same directory as the

vehicle.groovy.

To simplify the mapping, we used the same property names in both, JSON message and the

Vehicle class.

How to do it...

Create a new Groovy script, convert.groovy, making sure that it's located in the same folder as the JSON file.

1.Add the following code to the script:

import groovy.json.JsonSlurper import org.groovy.cookbook.Vehicle

def reader = new FileReader('vehicle.json')

def jsonVehicle = new JsonSlurper().parse(reader)

def vehicle = new Vehicle ( brand: jsonVehicle.brand,

215

www.it-ebooks.info

Working with JSON in Groovy

model: jsonVehicle.model,

transmission: new Vehicle.Transmission( gears: jsonVehicle.transmission.gears, type: jsonVehicle.transmission.type),

releaseYear: jsonVehicle.releaseYear, fuel: jsonVehicle.fuel)

println vehicle

2.Run the script in a usual way: groovy convert.groovy

3.The println command outputs:

org.groovy.cookbook.Vehicle(Mazda, 5, PETROL, 2007, org.groovy.cookbook.Vehicle$Transmission(5, MANUAL))

How it works...

Groovy performs some of the data type mapping automatically for us; making the conversion code simpler. For example, jsonVehicle.fuel is actually a String, but it is transformed to FuelType enumeration automatically since the values are matching. Also, jsonVehicle. transmission.gears is a String in our original JSON message (because it is included in the double quotes), but it is still safely converted to a long field in the Transmission object.

Thanks to the @ToString annotation, the Vehicle class shows all its internal data upon printing by the last line of the script.

There's more...

Writing the conversion code for every possible type you may need, is time consuming. Fortunately, existing third-party libraries come to the rescue. In the following sections, we will show how Groovy Bean conversion can be achieved with the JSON-lib, Jackson and GSON libraries. Please note that for the next examples to work the Vehicle class is imported, therefore, it must be extracted to a Vehicle.groovy class and added to the classpath when running the code.

ff JSON-lib can be used in the following way:

@Grapes([ @Grab(group='net.sf.json-lib',

module='json-lib', version='2.3', classifier='jdk15'),

@Grab('xom:xom:1.2.5')

])

216

www.it-ebooks.info

Chapter 6

import net.sf.json.JSONObject import org.groovy.cookbook.Vehicle

def file = new File('vehicle.json')

def jsonObject = JSONObject.fromObject(file.text)

println JSONObject.toBean(jsonObject, Vehicle)

ff Jackson can be used in the following way:

@Grab('com.fasterxml.jackson.core:jackson-databind:2.1.0') import com.fasterxml.jackson.databind.ObjectMapper

import org.groovy.cookbook.Vehicle

def mapper = new ObjectMapper()

def file = new File('vehicle.json')

println mapper.readValue(file, Vehicle)

ff GSON can be used in the following way:

@Grab('com.google.code.gson:gson:2.2.2') import com.google.gson.Gson

import org.groovy.cookbook.Vehicle

def gson = new Gson()

def reader = new FileReader('vehicle.json')

println gson.fromJson(reader, Vehicle)

All of those frameworks produce similar results, and they can be further tweaked for more complex data conversion requirements.

See also

ff Parsing JSON messages with JsonSlurper

ff http://json-lib.sourceforge.net/

ff http://code.google.com/p/google-gson/ ff http://wiki.fasterxml.com/JacksonHome

217

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]