JSON

Table of contents
  1. JSON
    1. External resources
    2. JSON data types
    3. Converting a simple DataClass into a json schema
    4. Lists and arrays
    5. Restricting values, Enums and Nullability
    6. Schema nesting

This section deals with the integration of json schema to validate GameActionClasses on the server side and how to generate a json schema from a given KClass.

External resources

A documentation for json schema can be found here.
A quick-start-guide can be found here.

JSON data types

The following data types may be used in json schema:

TypeValues allowedValues not allowed
string“foo”1
number1; 1.1; 1.1e5””
integer1; 1.01.1
objectNested objects 
array[“1”, “2”]; [1, 2]””; 1
booleantrue; false“true”; 1
nullnull””

Converting a simple DataClass into a json schema

Let’s consider the following data class:

@GameActionClass
data class MauMauEndGameAction(
  val winner: String
) : GameAction() {
	
  override fun toString(): String = 
    "$winner has won the game!"
}

The json schema contains all attributes declared in the class. In this instance there is only one field winner of type string. The resulting json schema looks as follows.

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "title": "MauMau End Game Schema",
	
  "required": [
    "winner"
  ],
	
  "properties": {
    "winner": { "type": "string" }
  },
	
  "additionalProperties": false
}

The first block defines meta information: The json meta schema draft-07, the type object and the title of this schema.

The required field contains a list of all attributes of the KClass. In this case the field winner.

For each field you have to define properties. This includes the data type for the field as one of those listed above. Here the property winner gets annotated with type string.

Additionally "additionalProperties": false forbids the usage of additional attributes.

Lists and arrays

For lists and arrays the data type is set to array. Additionally, the data type for the elements in that array has to be specified. The attribute

data class Example(
  val playerNames: list<String>
)

gets translated to

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "title": "Example Schema",
	
  "required": [
    "playerNames"
  ],
	
  "properties": {
    "playerNames": { 
      "type": "array",
      "items":{ "type": "string" }
    }
  },
	
  "additionalProperties": false
}

Restricting values, Enums and Nullability

Values of a field can be restricted by minimum/maximum for integers or by regular expression patterns.

Enums can be used by restricting the values of a string to the enum constants

enum class Direction {
  LEFT,
  RIGHT
}

data class Example(
  val direction: Direction
)

gets translated to

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "title": "Example Schema",
	
  "required": [
    "direction"
  ],
	
  "properties": {
    "playerNames": { 
      "type": "string",
      "enum": ["LEFT", "RIGHT"]
    }
  },
	
  "additionalProperties": false
}

Nullability can be expressed by using oneOf.

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "title": "Example Schema",
	
  "required": [
    "direction"
  ],
	
  "properties": {
    "playerNames": {
      "oneOf": [
        { "type": "null" },
        { "type": "string",
          "enum": ["LEFT", "RIGHT"] }
      ]
    }
  },
	
  "additionalProperties": false
}

Schema nesting

For attributes of object types, these objects need to be defined by a schema as well. Consider the following example:

enum class CardColor {
  RED,
  BLACK
}

data class PlayingCard(
  val cardColor: CardColor,
  val cardValue: Int
) 

data class CardStack(
  val cards: List<PlayingCard>
)

The class CardStack uses the class PlayingCard. Therefor the schema for PlayingCard has to be nested into the schema for CardStack.

The sub-schema for PlayingCard looks as follows:

{
  "type": "object",
	
  "required": [
    "cardColor",
    "cardValue"
  ],
	
  "properties": {
    "cardColor": {
      "type": "string",
      "enum": [
        "RED",
        "BLACK"
      ]
    },
    "cardValue":{
      "type": "integer",
      "minimum": 0,
      "maximum": 12
    } 
  },
	
  "additionalProperties": false
}

The schema for CardStack referencing the nested schema looks as follows:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "title": "Playing Card",
	
  "required": [
    "cards"
  ],
	
  "properties": {
    "cards": {
      "type": "array",
      "items": { "$ref": "#/definitions/playingCard" }
  }
  },
	
  "additionalProperties": false,

  "definitions": {
    "playingCard": {
      "type": "object",
	    
      "required": [
        "cardColor",
        "cardValue"
      ],

      "properties": {
        "cardColor": {
          "type": "string",
          "enum": [
            "RED",
            "BLACK"
          ]
        }, 
        "cardValue":{
          "type": "integer",
          "minimum": 0,
          "maximum": 12
        }
      }
    }
  }
}