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

Chapter 6

3.The next step is to save the modified message. There is the groovy.json. JsonOutput class, which is designed specifically for that purpose. By using the static toJson and prettyPrint methods we can get an indented text version of the modified message:

println JsonOutput.prettyPrint(JsonOutput.toJson(ui))

4.Our initial modifications will lead to the following output:

{

"items": [

{

"title": "Main Window", "animate": true, "height": 270, "width": 319, "insetPadding": 20, "type": "panel"

}

]

}

How it works...

As we are operating on a Map, the code above does not do anything else but adding and removing Map entries.

The toJson method returns a String with a compact version of the JSON data. The prettyPrint method adds additional indentation spaces to any given JSON string.

See also

ff Parsing JSON messages with JsonSlurper

ff http://groovy.codehaus.org/gapi/groovy/json/JsonSlurper.html

ff http://groovy.codehaus.org/gapi/groovy/json/JsonOutput.html

Validating JSON messages

JSON is replacing XML for many applications, but one of the features that XML is exceptionally good for is the ability to validate XML content against a DTD or an XML Schema.

205

www.it-ebooks.info

Working with JSON in Groovy

Due to the lightweight nature of JSON, it is quite simple to construct invalid or incomplete messages. That's why the necessity of JSON validation will arise quite soon if you plan to develop high-quality and error-free applications.

This recipe will list some ways to validate your JSON input with the help of Groovy.

Getting ready

For this recipe, we are going to use a simple JSON document representing a vehicle and some of its core attributes. Let's define a vehicle.json file containing a JSON representation of a car:

{

"brand": "Mazda", "model": "5", "fuel": "PETROL", "releaseYear": 2007, "transmission": {

"gears": "5", "type": "MANUAL"

}

}

How to do it...

Since JSON messages are represented by Maps and Lists, you can just use the Groovy operators and collection methods—or simple GPath expressions—to navigate and express the validation rules over a parsed JSON message. More information about GPath can be found in the Searching in XML with GPath recipe in Chapter 5, Working with XML in Groovy.

1.We begin by parsing our document in the same way we did it in previous recipes (for example, Parsing JSON messages with JsonSlurper):

import groovy.json.*

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

2.Now we can define the following validation functions in our script:

def isValidTransmission(vehicle) { vehicle?.transmission?.type in

[ 'MANUAL', 'AUTOMATIC' ] && vehicle?.transmission?.gears > 3

}

206

www.it-ebooks.info

Chapter 6

def isValidFuel(vehicle) { vehicle?.fuel in

[ 'DIESEL', 'PETROL', 'GAS', 'ELECTRIC']

}

def hasWheels(vehicle) { vehicle?.wheels?.size() > 0

}

3.Calling the validation methods as:

println 'Vehicle has valid transmission data: ' + isValidTransmission(vehicle)

println 'Vehicle

has valid fuel:

' +

isValidFuel(vehicle)

 

println 'Vehicle

has wheels:

' +

hasWheels(vehicle)

4. The previous script yields the following output:

Vehicle has valid transmission data: true

Vehicle

has

valid fuel:

true

Vehicle

has

wheels:

false

How it works...

The previous code makes use of several very useful Groovy operators, which make Boolean expressions look very short.

One of them is the safe navigation operator ?., which stops evaluating the Boolean expression and returns null immediately if left operand is null. That means no ugly nested if statements and no unexpected NullPointerExceptions, because the expression is not evaluated further and because null will yield false in Boolean context.

So, for example, vehicle?.transmission?.gears > 3 will return false if vehicle or transmission are null, or if transmission is not null, but there are no gears and so on.

207

www.it-ebooks.info

Working with JSON in Groovy

There's more...

Another approach would be to use JSON Schema. It is an emerging standard that is created by the JSON community and it is essentially targeted to offer a similar functionality to what XML Schema provides for XML.

JSON Schema, in the same fashion as XML Schema, can be expressed in JSON itself. To give you an idea of how it looks, for our vehicle data we can define the following schema in a file:

{

"$schema": "http://json-schema.org/draft-03/schema#", "description" : "Vehicle data schema",

"type" : "object", "properties" : {

"brand" : {

"type" : "string", "required" : true

},

"model" : {

"type" : "string", "required" : true

},

"fuel" : {

"type" : "string",

"enum" : ["DIESEL", "PETROL", "GAS", "ELECTRIC"]

},

"releaseYear" : { "type" : "integer"

},

"transmission" : { "type" : "object",

"properties" : { "gears": {

"type" : "integer"

}, "type": {

"type" : "string",

"enum" : ["MANUAL", "AUTOMATIC"]

}

}

}

}

}

208

www.it-ebooks.info

Chapter 6

Basically, a JSON Schema defines a JSON object structure with the help of standard property names and conventions, for example, the type attribute defines the type (for example, String, integer, date, object, array, and so on) of the structure appearing on the same level in JSON message, properties attribute contains a collection of nested property definitions, which also describe type, name and restrictions like enum or required of the property value.

Please note that the JSON Schema is still a moving target: the example above uses the slightly older draft 3 format. A newer version, named draft 4, is available.

Currently there aren't many implementations of the JSON Schema in Java/Groovy. For this example, we are going to use an implementation called json-schema-validator.

@Grab('com.github.fge:json-schema-validator:2.0.1') import com.github.fge.jsonschema.main.JsonSchemaFactory import com.github.fge.jsonschema.report.ProcessingReport import com.github.fge.jsonschema.util.JsonLoader

import com.github.fge.jsonschema.main.JsonSchema

def factory = JsonSchemaFactory.byDefault()

def schemaFile = new File('vehicle_schema.json') def metadata = JsonLoader.fromFile(schemaFile)

def data = JsonLoader.fromFile(new File('vehicle.json'))

def schema = factory.getJsonSchema(metadata)

def report = schema.validate(data)

def success = report.isSuccess()

println("Validation ${success ? 'succeeded' : 'failed'}")

if (!success) {

println('---- BEGIN REPORT ----') report.each { message -> println message } println('---- END REPORT ----')

}

First of all, we Grab-ed the dependency for json-schema-validator and imported several required classes. The json-schema-validator library internally uses the JsonNode data structure to keep the JSON data. For this reason, we need to rely on JsonLoader to load the

JSON files.

209

www.it-ebooks.info

Working with JSON in Groovy

Noticeably, data and schema are loaded in the same way. After constructing a JsonSchema object using JsonSchemaFactory, we finally can validate the data. The last code lines just call the validation routine and print out validation messages, if any.

The script reports one error (the following output is formatted for readability):

Validation failed

----BEGIN REPORT ----

{

level="error",

schema={

"loadingURI":"#",

"pointer":"/properties/transmission/properties/gears"

},

instance={"pointer":"/transmission/gears"},

domain="validation",

keyword="type",

message="instance type does not match any allowed primitive type",

expected=["integer"],

found="string"

}

----END REPORT ----

The error reported by the validator is related to /transmission/gears: the JSON validation code expects an Integer, but the JSON document provides a String:

{

...

"transmission": { "gears": "5", "type": "MANUAL"

}

...

}

JSON Schema provides a more generic approach to validation error handling compared to the custom Groovy code.

210

www.it-ebooks.info

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