Packages

  • package root

    Documentation for the Axiell Schema Project

    Documentation for the Axiell Schema Project

    Overview

    The Axiell Schema Project provides a small library for validating JSON documents against a JSON Schema. The library conforms to the JSON Schema Draft #7 specification.

    The library is based on a few simple principles, namely:

    • referential transparency. Each of the methods in the library is referentially transparent, that is if the method is called with the same arguments it will always return the same value and the method does not have any side effect (e.g. update globals, change the operating environment, etc).
    • minimal functionality. The library is designed to be small incorporating a small number of dependencies. The number of methods associated with a class/object is restricted to those that provide a base level of functionality.
    • support multiple JSON libraries. It should be possible to use the library with any number of different JSON library packages (e.g json4s, jackson, etc). The current library only supports json4s, but a facade approach should be added to hide individual library implementations.
    • generic "format" implementations. The JSON Schema specification mandates a standard set of "format" types that can be used to validate string values. Each "format" type is implemented as a class with the components of the type available.
    • full error handling. Error resulting from the validation process are returned as a list of validation failures. All possible errors should be returned for a single call, eliminating the need to fix a single error and then recall a method.

    Package structure

    The com.axiell.schema packages consists of a number of utilities, where each utility provides a single piece of functionality. Since the library validates JSON structures two main components are provided. The first component (com.axiell.schema.Schema) allows JSON documents conforming to the JSON Schema specification to be read. Once read a resolution process may be required to resolve any external references defined in the document.

    The second component (com.axiell.schema.SchemaValidator) takes a resolved schema and allows JSON documents to be validated against that schema. When creating a validator it is also possible to incorporate your own "format" types for validation.

    A set of auxiliary components implement parsers for many of the "format" types and can be used independently of the validator itself.

    Notable utilities are:

    • Schema wrapper around a JSON document containing the document itself, its $id value and resolution mappings for all $id and $ref directives.
    • SchemaValidator takes a Schema and an optional handler for "format" types and provides a validation method for arbitrary JSON documents.
    • Implicits implements implicit methods and classes used to convert one class to another or pimp methods onto existing classes (e.g. same() method to JValue class).
    • various auxiliary classes for handling "format" types including:

    Dependencies

    The list of dependencies are:

    • Axiell Util Library provides error handling classes and methods that allow multilingual error messages to be generated.
    • JSON for Scala Library provides mthods for dealing with JSON structures. The routines work around an AST (Abstract Syntax Tree) used to represent a JSON document.
    • Cats Functional Programming Library provides various traits, classes and methods that simplify functional programming in Scala. Also provides the Validated class used to return lists of validation errors.
    Definition Classes
    root
  • package com
    Definition Classes
    root
  • package axiell
    Definition Classes
    com
  • package schema

    Contains types used by the library.

    Contains types used by the library.

    In order to simplify method signatures a series of types are defined. The types are used throughout the library.

    Definition Classes
    axiell
  • package instances
  • package syntax
  • Assertions
  • Context
  • EmailAddress
  • Formats
  • Hostname
  • Implicits
  • Ipv4Address
  • Ipv6Address
  • JsonPointer
  • RelativeJsonPointer
  • Resolver
  • Schema
  • SchemaValidator
  • SchemaWalker
  • Validator
p

com.axiell

schema

package schema

Contains types used by the library.

In order to simplify method signatures a series of types are defined. The types are used throughout the library.

Source
package.scala
Linear Supertypes
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. schema
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. Protected

Package Members

  1. package instances
  2. package syntax

Type Members

  1. final case class Context(baseUri: URI, path: JsonPointer, schemaPath: JsonPointer, readOnly: Boolean, refs: Refs, formats: Formats) extends Product with Serializable

    Contains context information as validator is executed.

    Contains context information as validator is executed.

    When schema validation is invoked the JSON Schema document is traversed and as the traversal occurs the JSON data is matched against the relevant constraints. The traversal of the Schema document needs to track the current base URI so it can resolve relative $id properties in the document itself. The paths of the current constraint and data are also maintained so that error messages can indicate the location within the data and also the location of the constraint of a failure.

    Other environment based information is kept to avoid passing implicit values to all validation methods. The information includes whether readOnly properties should be honoured and also a partial function used to validate format properties.

    Since the context is only used internally by the validation routines it is declare to be private within the project.

    baseUri

    for the current JSON Schema node

    path

    of the current JSON data node

    schemaPath

    of the current JSON Schema node

    readOnly

    true if the readOnly property should be honoured

    refs

    map containing reference resolution entries

    formats

    partial function used to validate format properties

  2. final case class EmailAddress extends Product with Serializable

    Contains the local and domain parts of an email address.

    Contains the local and domain parts of an email address.

    The local part consists of the user name or group to deliver the email to on the local machine. The domain part is the fully qualified machine name to which the mail is to be delivered. Both the local and domain values are set after any escaping has been processed. Any comments in the address have been removed.

    For example the email address:

    "fred.smith"@example.com (Fred Smith)

    will result in local being set to fred.smith and domain set to example.com. The comment (text enclosed in brackets) has been removed.

  3. type Formats = PartialFunction[(String, String), Option[Error]]

    Type of partial function required to handle format properties.

    Type of partial function required to handle format properties.

    The JSON Schema format property allows a type to be set for JSON string values. The JSON Schema Draft #7 defines a set of format types that should be supported. It is possible to extend this list and define your own formats by providing a partial function to handle your formats. See the withFormats method on SchemaValidator.

    The partial function takes a tuple containing the format type as a string and the value as a string. The function should return scala.None if the format validation succeeds, otherwise an Error should be returned wrapped in a Some.

  4. case class Hostname extends Product with Serializable

    Contains a valid hostname.

    Contains a valid hostname.

    The definition of a valid hostname can be found in RFC 1034, section 3.1.

    See also

    "RFC 1034, section 3.1"

  5. type Ids = Map[URI, JObject]

    Type of $$id map maintained with a schema.

    Type of $$id map maintained with a schema.

    Each Schema contains a map of fully qualified $$id URI values and its corresponding position in the JSON Schema. The map is used when resolving $ref references to locate URI definitions in a schema.

  6. final case class Ipv4Address extends Product with Serializable

    Contains an array of four numbers representing an IPv4 address.

    Contains an array of four numbers representing an IPv4 address.

    An IPv4 address consists of four unsigned bytes with values between 0 and 255. The bytes are represented as an array of short values as Java does not support an unsigned byte type. The array has the most significant value in index zero, with the least significant value in index three.

  7. final case class Ipv6Address extends Product with Serializable

    Contains an IPv6 address as eight integer values.

    Contains an IPv6 address as eight integer values.

    An IPv6 address is of the form:

    a:b:c:d:e:f:g:h

    where each component is a sixteen bit number. In hex between 0 and ffff. The address is stored as an array of eight numbers where each number represents a single component.

    For example the address:

    2001:DB8:0:0:8:800:200C:417A

    would be stored as an array of eight values:

    Array(0x2001, 0xD88, 0x0, 0x0, 0x8, 0x800, 0x200C, 0x417A)
  8. final case class JsonPointer extends Product with Serializable

    Contains the components making up a JSON Pointer.

    Contains the components making up a JSON Pointer.

    The components that make up the path in a JSON Pointer are stored as a vector. Each component has had any escape characters replaced with the underlying character (~0 => ~, ~1 => /). A series of methods are provided that allow paths to be built and resolved.

    The root path is represented by an empty JSON pointer:

    JsonPointer()

    Paths can be built up. For example

    JsonPointer() / "items" / 0 / "name"

    refers to the name property stored in the zeroth element of the items array.

    Paths can be resolved against a JSON document:

    JsonPointer.fromString("items/0/name").resolve(
      parse("""
        {
          "items": [
            {"name": "spanner"},
            {"name", "hammer"},
            {"name", "chisel"
          ]
        }"""
      )
    )

    would resolve to the value "spanner".

  9. type Refs = Map[URI, Option[JValue]]

    Type of $$ref map maintained with a schema.

    Type of $$ref map maintained with a schema.

    Each Schema contains a map for each fully qualified $$ref URI value and the position in a JSON Schema referenced by that URI. When a schema is read only local references are resolved, with external references being resolved when an external schema is resolved against the current schema. See the resolve method on Schema. Since an reference may not be resolved its location is defined as an scala.Option.

  10. final case class RelativeJsonPointer extends Product with Serializable

    Contains the components that make up a relative JSON Pointer.

    Contains the components that make up a relative JSON Pointer.

    A relative JSON Pointer is defined as a non-negative integer followed by either a JSON Pointer or a #. The number specifies the number of parent nodes to traverse before applying either a JSON Pointer path or in the case of a # output the current array index if in a JSON array or property key if in a JSON object.

    Example:

    {
      "foo": ["bar", "baz"],
      "highly": {
        "nested": {
          "objects": true
        }
      }
    }

    Starting from the value "baz" (inside "foo"), the following JSON strings evaluate to the accompanying values:

    "0"                         "baz"
    "1/0"                       "bar"
    "2/highly/nested/objects"   true
    "0#"                        1
    "1#"                        "foo"
  11. type Result[A] = Validated[NonEmptyList[Error], A]

    Generic type returned from validation and schema methods.

    Generic type returned from validation and schema methods.

    When a schema is loaded a number of errors my occur. These errors include bad URI identifiers and references. In order to allow multiple errors to be returned the Validated type is used with the error component defined as a list of Error errors. In the case of validation a list of errors is also generated and stored as a list of Error errors.

  12. case class Schema extends Product with Serializable

    Wraps a JSON Schema compliant tree.

    Wraps a JSON Schema compliant tree.

    A JSON Schema tree contains the definition of a schema used to validate a JSON data structure. In order to provide efficient validation and also fast navigation of the JSON Schema tree, all $id properties are extracted into a map and all $ref properties are resolved against that map where possible. Since a $ref may refer to an external schema it may not be possible to resolve all references when wrapping the JSON Schema.

    The resolution of all $ref references is achieved by calling the resolve() method with external JSON Schema documents. Where a reference can be resolved against the external document the entry in the refs map is updated. The refs map contains as a key the fully qualified URI specified by the $ref reference and the value points to a part of a JSON Schema resolved against the reference. If a reference has not been resolved its value is None and where resolved a Some value containing the referenced JSON Schema is found.

    When a JSON Schema is wrapped any internal references are resolved automatically, only external references need to added via the resolve() method. Consider the following three JSON Schema documents:

    val schema1 = parse("""
      {
        "$id": "cdp:/test/resources/reftableone#",
        "definitions": {
          "date": {
            "$id": "#date",
            "type": "string",
            "format": "partial-date"
          }
        }
      }"""
    )
    val schema2 = parse("""
      {
        "$id": "cdp:/test/resources/reftabletwo#",
        "definitions": {
          "start": {
            "$ref":"cdp:/test/resources/reftableone#date"
          },
          "end": {
            "$ref":"cdp:/test/resources/reftableone#date"
          }
        }
      }"""
    )
    val schema3 = parse("""
      {
        "$id": "cdp:/test/resources/reftablethree#",
        "properties": {
          "begin": {
            "$ref": "cdp:/test/resources/reftabletwo#/definitions/start"
          },
          "finish": {
            "$ref": "cdp:/test/resources/reftabletwo#/definitions/end"
          }
        }
      }"""
    )

    The following code is required to wrap schema3 and resolve all references:

    Schema(schema1).andThen { one =>
      Schema(schema2).andThen { two =>
        Schema(schema3).andThen { three =>
          three.resolve(two)
            .andThen(_.resolve(one))
        }
      }
    }

    The result of the above code is a wrapped schema3 with all references resolved. Note the order of resolution is important. schema2 is resolved against schema3 first (as it resolves the two references to schema2, but also introduces two new references to schema1), then schema1 is resolved (as it resolved the two references added by schema2).

    A list of unresolved references can be retrieved via the unresolved() method. The method returns a list of unresolved fully qualified URI values. The isResolved method will return true if all references have been resolved, otherwise false is returned.

    When a JSON Schema is wrapped its root $id value is extracted as a property. If a root $id URI is not specified then one is generated. The format of the generated URI is:

    ni:///md5;<md5 checksum>

    The <md5 checksum> is generated by converting the JSON Schema to a string and computing the string's md5 checksum. Such an approach ensures JSON Schema's that do not contain a root $id URI end up with a unique root URI.

  13. type SchemaResult = Validated[NonEmptyList[Error], Schema]

    Specific result type for loading JSON Schemas.

    Specific result type for loading JSON Schemas.

    When a JSON Schema is loaded either a Schema is created or a list of error is returned. This type defines the return type used when loading schemas.

  14. case class SchemaValidator extends Product with Serializable

    Allows a JSON Schema to be used to validate a JSON data object.

    Allows a JSON Schema to be used to validate a JSON data object.

    A validator wraps a Schema along with the formats partial function to allows JSON data object to be validated. The use of a partial function for resolving different formats allows third party formats to be supported.

    An example use is:

    val schema = parse("""
      {
        "properties": {
          "foo": {"type": "integer"},
          "bar": {"type": "string"}
        }
      }"""
    )
    val validator = Schema(schema).andThen(SchemaValidator(_))
    val result = validator.validate(data).isValid

    It is also possible to add in extra format values using a partial function:

    private implicit val bundle = MessagesBundle("com.example.Resources")
    val formats: PartialFunction[(String, String), Option[Message]] = {
      case ("phonenumber", value) =>
        "^[0-9]{8}$".r.findFirstIn(value) match {
          case None => Some(Message("BadPhoneNubmer", value))
          case Some(_) => None
        }
    }
    val validator = Schema(schema)
      .andThen(SchemaValidator(_).withFormats(formats))

    A standard set of formats are provided based on the JSON Schema Draft #7. In order to support formats outside the standard the withFormats() method should be provided with a partial function to cater for extra formats.

  15. abstract class SchemaWalker[A] extends AnyRef

    Provides a "visitor" pattern to walk a JSON Schema document.

    Provides a "visitor" pattern to walk a JSON Schema document.

    The walk method visits each node in a JSON Schema document. For each JSON Object encountered it invokes the abstract process method for each property defined within the JSON object. Along with each property name and value it passes in the JSON object containing the property and the base URI for the object. The base URI is determined by processing $$id properties found in the JSON Schema.

    Users should implement the abstract process method if they want to handle properties stored in a JSON Schema document. The method should return either an error in Error or a value defined by the A type generic. Since process accumulates results the type A must implement a Semigroup implicit value to combine A elements.

    Example:

    val walker = new SchemaWalker[Unit] {
      def process((path: JsonPointer, baseUri: URI, parent: JObject,
          name: String, value: JValue): Validated[Error, Unit] =
        (name, value) match {
          case ("$ref", JString(uri)) =>
            println(s"Found reference ${baseUri.resolve(uri)}")
          case _ =>
        }
    }
    
    walker.walk(new Uri(), parse("""{ <JSON Schema Document> }"""))
    A

    type returned from process method

  16. type ValidatorResult = Validated[NonEmptyList[Error], Unit]

    Specific result type for JSON validation.

    Specific result type for JSON validation.

    When a JSON Schema is validated against a JSON document the result is either a list of errors or Unit. If Unit is returned then the validation succeeded.

Value Members

  1. object Assertions

    Implements all the assertions required for JSON Schema Draft #7

    Implements all the assertions required for JSON Schema Draft #7

    A JSON Schema document is made up of a number of assertions. The application of the assertions depends on the structure and content of the JSON data object being validated. Each assertion is implemented as a method in this object. The assertions are called from the Validator object.

    Each assertion returns a ValidatorResult which contains either a Valid Unit value indicating the assertion was met, or an Invalid value containing a NonEmptyList of Error messages detailing why the assertion failed.

  2. object EmailAddress extends Serializable

    Creates an EmailAddress by parsing an email address string.

    Creates an EmailAddress by parsing an email address string.

    The parser conforms to both RFC 5322, section 3.4.1, which outlines what is a valid email address using the ASCII character set and RFC 6531 which contains extensions allowing for Unicode based email addresses.

    The parser will strip out any comments (enclosed in backets) from the email address and divide the address into two parts. The first part is the local section which occurs before the @ symbol and the second part is the domain which is after the @ symbol. Any escapes are interpreted and removed from the values.

    See also

    "RFC 5322, section 3.4.1"

    "RFC 6531"

  3. object Formats

    Implements the standard set of string based format types.

    Implements the standard set of string based format types.

    JSON Schema Draft #7 defines a format property for values of type string. The format allows complex types, like date, time, etc to be checked even though the JSON value itself is still a string. Each of the required formats is available with implementations for most types.

    Support is not provided for:

    • iri
    • irireference
    • uritemplate

    All other required formats are implemented according to the relevant RFC. The validation of the format property is performed by a partial function. The type is:

    PartialFunction[(String, String), Option[Error]]

    The input tuple consists of the format name and the value. For example:

    ("email", "fred.smith@axample.com")

    The return value should be None if validation passes otherwise an Error wrapped in a Some indicating what was the error should be returned. The format method is a partial function that handles all the types defined in JSON Schema Draft #7.

    See also

    "JSON Schema Draft #7"

  4. object Hostname extends Serializable

    Contains constructors to parse a hostname.

    Contains constructors to parse a hostname.

    A parser executes over a supplied hostname to ensure it conforms to the specifications defined in RFC 1034, section 3.1. The parser can operate in one of two modes. The first follows RFC 1034 in allowing only ASCII based names, while the second mode follows RFC 5890 which allows for Unicode based hostnames.

  5. object Implicits extends JsonPointerInstances with JValueInstances with NonEmptyListInstances with ErrorSyntax with JValueSyntax

    Groups all implicits into a single object

    Groups all implicits into a single object

    Method extension implicits (Syntax suffix) and implicit variables (Instances Suffix) are mixed into a single object allowing a single import statement to be used to bring them all into scope.

    The required import statement is:

    import com.axiell.schema.Implicits._
  6. object Ipv4Address extends Serializable

    Contains parser to validate an IPv4 address into four valid bytes.

    Contains parser to validate an IPv4 address into four valid bytes.

    The parser conforms to RFC 5890, section 2.3.2.3. The format of the IP address as a string should be:

    a.b.c.d

    where a, b, c, and d are numbers between 0 and 255.

  7. object Ipv6Address extends Serializable

    Contains parser for IPv6 addresses.

    Contains parser for IPv6 addresses.

    The parser takes a string representing an IPv6 address and creates an Ipv6Address object if the address is valid. The address can be in any of the text forms defined in RFC 4291, section 2.2.

    The three forms supported are:

    • full address - all components are specified
    e.g. 2001:DB8:0:0:8:800:200C:417A
    • abbreviated address - use of :: for zero sequences
    e.g. 2001:DB8::8:800:200C:417A
    • mixed IPv4 address - use of IPv4 address for last two components
    e.g. 2001:DB8::8:800:32.12.65.122

    In all cases the resulting Ipv6Address object will pad out the address to its eight components.

  8. object JsonPointer extends Serializable

    Constructs JSON Pointer objects.

    Constructs JSON Pointer objects.

    There are three ways to construct a JSON Pointer object. The first is to create an empty JsonPointer and use the append methods to add path components to the pointer.

    Example:

    JsonPointer() / "items" / 0 / "name"

    The second method is to pass individual path components to the JsonPointer constructor.

    Example:

    JsonPointer("items", "0", "name")

    Notice the constructor only takes string components so any array index values must be provided as a string.

    The third method is to provide the path as a string and have it parsed into a JsonPointer. The string must be in the escaped form, that is ~ => ~0 and / => ~1.

    Example:

    JsonPointer.fromString("/items/0/name")
  9. object RelativeJsonPointer extends Serializable

    Parses string representing a Relative JSON Pointer.

    Parses string representing a Relative JSON Pointer.

    The parser conforms to the draft RFC for Relative JSON Pointers. The parser is quite simple looking for a non-negative number followed by either a valid JSON Pointer or a # character. Any other format will fail.

    See also

    "https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01"

  10. object Resolver

    Resolves $$ref references against a given JSON Schema.

    Resolves $$ref references against a given JSON Schema.

    JSON Schema documents may contain $$ref properties. The property allows another part of a JSON Schema to be referenced from the current location. It is in essence an import or include mechanism. The value of a $$ref property is a URI referring to a schema and a location within the schema. When a JSON Schema document is loaded the $$ref references need to be resolved to ensure that the validation process can follow references without error.

    The resolve method performs reference resolution against a given JSON Schema. Each Schema has a table listing all references in the Schema document. The resolution routine updates that table based on the contents of a supplied schema. Such an approach allows references to external schemas to exist. To resolve such external references the resolution method must be called with the external schema. As more schemas are resolved against the references table the number of unresolved references should diminish. Once all references are resolved validation can be performed without an undefined reference error being generated.

  11. object Schema extends Serializable

    Wraps a JSON Schema document in a Schema.

    Wraps a JSON Schema document in a Schema.

    The wrapping process consists of three parts. The first part is the extraction of the root $id URI from the document. If the document does not contain a root $id value then one is generated. The second part is the extraction of all $id values from the document into a map containing the fully qualified version of the URI along with a link to its location in the document. The third part is the resolution of any $ref properties in the JSON Schema document. The references are extracted into the refs map.

  12. object SchemaValidator extends Serializable

    Create a validator for the supplied Schema.

    Create a validator for the supplied Schema.

    A validator is constructed suitable for validating data against the supplied JSON Schema document. The document should conform to the JSON Schema Draft #7 standard. Once a validator is constructed it can be used to validate any number of JSON data objects.

    The supplied schema does not have to have all its references fully resolved, however an error will be generated if while validating an unresolved reference is encountered.

  13. object Validator

    Validates a JSON document against a JSON Schema.

    Validates a JSON document against a JSON Schema.

    The validator conforms to the JSON Schema Draft #7 standard. Any validation errors are returned in a Validated as a NonEmptyList. Each element in the list is an Error containing a description of the error, the path in the JSON document where the error occurred and the path in the JSON schema document where validation failed.

    The validation process is carried out by a SchemaValidator. The validation methods in this object are called by the SchemaValidator object.

Inherited from AnyRef

Inherited from Any

Ungrouped