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.

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
    }
    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.

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"]
         }
      }
    }
  }
}

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.

Note

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.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.116.51