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.
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 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.
Create a new Groovy script, convert.groovy
, making sure that it's located in the same folder as the JSON file.
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, model: jsonVehicle.model, transmission: new Vehicle.Transmission( gears: jsonVehicle.transmission.gears, type: jsonVehicle.transmission.type), releaseYear: jsonVehicle.releaseYear, fuel: jsonVehicle.fuel) println vehicle
groovy convert.groovy
println
command outputs:org.groovy.cookbook.Vehicle(Mazda, 5, PETROL, 2007, org.groovy.cookbook.Vehicle$Transmission(5, MANUAL))
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.
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.
@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 org.groovy.cookbook.Vehicle def file = new File('vehicle.json') def jsonObject = JSONObject.fromObject(file.text) println JSONObject.toBean(jsonObject, Vehicle)
@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)
@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.