Conditional JSON properties (Part 1)

Jonathan Stoikovitch
RAML by Example
3 min readJul 19, 2018

--

One of the most important aspect of designing Web APIs is modeling its data layer. The data layer can take different shapes and it can be found at different levels of the API: URI parameters, query parameters, request and response headers, request and response bodies.

When modeling a JSON object — the most common format for transmitting data these days — we may want sets of properties to be either valid or invalid depending on the presence or the absence of other properties.

Let’s take a look at different scenarios.

All and only defined properties

This is the most common one. We want an object to contain all and only defined properties, here foo and bar. These properties must be respectivly string and boolean.

This object can be defined like this:

#%RAML 1.0 DataType

additionalProperties: false
properties:
foo: string
bar: boolean

which validates as follows:

{"foo": "lorem ipsum", "bar": false} # valid
{"foo": true, "bar": false} # invalid
{"foo": "lorem ipsum"} # invalid
{"bar": "lorem ipsum", "foo": false} # invalid
{"qux": 1} # invalid
{} # invalid

Any of either defined or undefined properties

In some cases, we want to allow an object to contain any number of properties. However, if some specific properties are defined, here foo and bar, we want those properties to be of a certain type.

This object can be defined like this:

#%RAML 1.0 DataType

properties:
foo?: string
bar?: boolean

which validates as follows:

{"foo": "lorem ipsum", "bar": false} # valid
{"foo": true, "bar": false} # invalid
{"foo": "lorem ipsum"} # valid
{"bar": "lorem ipsum", "foo": false} # invalid
{"qux": 1} # valid
{"foo": null, "bar": null} # invalid
{} # valid

Making sure at least one property is defined

Building on the previous scenario, we want all of the above conditions and we also want to ensure at least one property is defined.

This object can be defined like this:

#%RAML 1.0 DataType

minProperties: 1
properties:
foo?: string
bar?: boolean

which validates as follows:

{"foo": "lorem ipsum", "bar": false} # valid
{"foo": true, "bar": false} # invalid
{"foo": "lorem ipsum"} # valid
{"bar": "lorem ipsum", "foo": false} # invalid
{"qux": 1} # valid
{} # invalid

Any of only defined properties

Building again on the previous scenario, we want all of the above defined conditions but we also want to restrict the properties to only the ones defined.

This object can be defined like this:

#%RAML 1.0 DataType

additionalProperties: false
minProperties: 1
properties:
foo?: string
bar?: boolean

which validates as follows:

{"foo": "lorem ipsum", "bar": false} # valid
{"foo": true, "bar": false} # invalid
{"foo": "lorem ipsum"} # valid
{"bar": "lorem ipsum", "foo": false} # invalid
{"qux": 1} # invalid
{} # invalid

Any defined properties, any defined types

Here, we want the object to contain at least one property, either foo or bar, but no additional properties. Either property can be string or boolean.

This object can be defined like this:

#%RAML 1.0 DataType

minProperties: 1
properties:
/^(foo|bar)$/: string | boolean

which is equivalent to:

#%RAML 1.0 DataType

additionalProperties: false
minProperties: 1
properties:
foo?: string | boolean
bar?: string | boolean

and which validates as follows:

{"foo": "lorem ipsum", "bar": false} # valid
{"foo": true, "bar": false} # valid
{"foo": "lorem ipsum"} # valid
{"bar": "lorem ipsum", "foo": false} # valid
{"qux": 1} # invalid
{} # invalid

Mirror properties

In this scenario, we want the object to have at least two properties foo and bar, which may be either string or boolean, but never at the same time.

This object can be defined like this:

#%RAML 1.0 Library

Option1:
properties:
foo: string
bar: boolean
Option2:
properties:
foo: boolean
bar: string
MyObject: Option1 | Option2

which validates as follows:

{"foo": "lorem ipsum", "bar": false} # valid
{"foo": true, "bar": false} # invalid
{"foo": "lorem ipsum"} # invalid
{"bar": "lorem ipsum", "foo": false} # valid
{"qux": 1} # invalid
{} # invalid

--

--