Introduction
It is assumed that the reader has read and understood the first part of this two-part article. If this is the case please proceed with the rest of this work.
Table of Contents
As with Part 1, the JSON concepts and how they are implemented are described using code fragments. Some of the examples used here are based on the work of Michael Droettboom and others of the Space Telescope Science Institute.
Consider the JSON schema text above. Observe that the three properties of the top level schema i.e., integerOne
, integerTwo
and integerThree
all have the same parameters. Regardless, each of these properties had to be individually created thus violating the DRY principle. The JSON schema specification attempts to mitigate this definition duplication using three mechanisms. These mechanisms allow object parameters to be defined outside an object and these defined parameters are then referenced within the objects. At run-time, the references are replaced by the parameters being referenced.
For this work the three referencing techniques are called Internal, Definitions and External.
Above is an example of a schema implementing Internal referencing. As can be seen, the parameters are defined once in the integerOne property and then a reference using the $ref
keyword with the identity of IntegerOne
is placed inside the other two properties. Functionally, the schema text above is similar to the schema text in the Example_2_1
routine. To check if this is true, deserialize the JSON text above and then serialize it again. Print out the serialized text to confirm that this is the case.
The C# code below is used to produce the schema text shown above.
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"refExamples", "REF Demo Schema", "ExampleID-022");
JsonSchemaObject _json_schema_integerOne = new JsonSchemaObject ("integerOne", JSON_TYPE.INTEGER,200, 500, 20, string.Empty, true);
_json_schema_integerOne.ExclusiveMinimum = true;
_json_schema_integerOne.ExclusiveMaximum = true;
_json_schema_integerOne.ObjectID = "integer_id_001";
JsonSchemaObject _json_schema_integerTwo = new JsonSchemaObject("integerTwo",
JSON_POINTER_TYPE.INTERNAL,_json_schema_integerOne.ObjectID,string.Empty,true);
JsonSchemaObject _json_schema_integerThree = new JsonSchemaObject("integerThree",
JSON_POINTER_TYPE.INTERNAL,_json_schema_integerOne.ObjectID,string.Empty,true);
_json_schema_object.AddObjects (new List<JsonSchemaObject> (new JsonSchemaObject [] {
_json_schema_integerOne,
_json_schema_integerTwo,
_json_schema_integerThree})
);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
Notice that integerTwo and IntegerThree are instantiated using the reference constructor i.e., JsonSchemaObject (string object_name,JSON_POINTER_TYPE ref_type,string json_schema_reference_uri,string description,bool is_required = false)
. Along with the object name, this constructor is passed the type of reference, in the case JSON_POINTER_TYPE.INTERNAL
, and the identity of the object being referenced i.e., _json_schema_integerOne.ObjectID
.
An exception is thrown during the deserialization process if the object referenced cannot be found.
The reference type and its associated URI can be read from the SchemaReferenceType
and SchemaReferenceUri
properties of the JsonSchemaObject class.
Definitions Referencing
In this case, defined schema parameters are placed inside special object called definitions
. The reference to parameters is placed inside each of the relevant objects as shown above for the IntegerOne
, integerTwo
and integerThree
properties.
The code used to accomplish the above is as shown below.
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"refExamples","REF Demo Schema", "ExampleID-023");
JsonSchemaObject _json_schema_integerDef = new JsonSchemaObject ("integerDef",
JSON_TYPE.INTEGER,200, 500, 20, string.Empty, true);
_json_schema_integerDef.ExclusiveMinimum = true;
_json_schema_integerDef.ExclusiveMaximum = true;
_json_schema_object.AddDefinition (_json_schema_integerDef);
JsonSchemaObject _json_schema_integerOne = new JsonSchemaObject ("integerOne",
JSON_POINTER_TYPE.DEFINITIONS,_json_schema_integerDef.ObjectName, string.Empty, true);
JsonSchemaObject _json_schema_integerTwo = new JsonSchemaObject ("integerTwo",
JSON_POINTER_TYPE.DEFINITIONS, _json_schema_integerDef.ObjectName, string.Empty, true);
JsonSchemaObject _json_schema_integerThree = new JsonSchemaObject ("integerThree",
JSON_POINTER_TYPE.DEFINITIONS, _json_schema_integerDef.ObjectName, string.Empty, true);
_json_schema_object.AddObjects (new List<jsonschemaobject> (new JsonSchemaObject [] {
_json_schema_integerOne,
_json_schema_integerTwo,
_json_schema_integerThree})
);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
Again, the JsonSchemaObject (string object_name,JSON_POINTER_TYPE ref_type,string json_schema_reference_uri,string description,bool is_required = false) constructor is used but this time it is passed the JSON_POINTER_TYPE.DEFINITIONS
as a parameter. Also, rather than passing the object ID as was done for Internal referencing, the object name i.e., integerDef
, is used instead.
Another example of a definitions URI is "$ref": "#/definitions/jsonObject/integerDef"
. This URI says that the integerDef is inside a JSON Object type called jsonObject and jsonObject, in turn, is inside of the definitions
object.
An exception is thrown during the deserialization process if the object referenced cannot be found in the all definitions objects within the scope or the parent scope of the object that contains the $ref attribute.
The reference type and its associated URI can be read from the SchemaReferenceType
and SchemaReferenceUri
properties of the JsonSchemaObject class.
In the Internal and Definitions referencing mechanism discussed so far, the schema being referenced is contained locally within the same object as the referred object. JSON allows for the referencing of a schema that resides outside an object, hence the term External referencing.
A typical external schema will look similar in structure to the JSON text above. Note that the identity of the top level object is a link i.e., "http://x.y.z/rootschema.json#"
. Referring to the integerExternal
from outside is done using this link and the identity of integerExternal as shown below.
The code used to accomplish the above is contained in the Example_2_4
routine. See below for excerpts.
/Example_2_4
string _object_id = "integer_id_024";
string _ref_schema_id = "http://x.y.z/rootschema.json#";
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"refExamples","REF Demo Schema", "ExampleID-024");
JsonSchemaObject _json_schema_integerOne = new JsonSchemaObject ("integerOne",
JSON_POINTER_TYPE.EXTERNAL, _ref_schema_id + _object_id, string.Empty, true);
JsonSchemaObject _json_schema_integerTwo = new JsonSchemaObject ("integerTwo",
JSON_POINTER_TYPE.EXTERNAL, _ref_schema_id + _object_id, string.Empty, true);
JsonSchemaObject _json_schema_integerThree = new JsonSchemaObject ("integerThree",
JSON_POINTER_TYPE.EXTERNAL, _ref_schema_id + _object_id, string.Empty, true);
_json_schema_object.AddObjects (
new List<JsonSchemaObject> (new JsonSchemaObject [] {
_json_schema_integerOne,
_json_schema_integerTwo,
_json_schema_integerThree})
);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
...
_json_schema_engine.AddSchemaRef (_json_schema_external_object);
JsonSchemaObject _json_schema_object_deserialized = _json_schema_engine.Deserialize (
_schema_string);
...
If an object being referenced is within an external schema, it is the duty of the application to retrieve this external schema before the deserialization routine is called. Otherwise, an exception is thrown during deserialization as the schema will not be found. Use the any of the four AddSchemaRef
routine to add external schemas (or schemata) before calling the Deserialize
routine. See the Example_2_4
routine on how this is implemented.
The reference type and its associated URI can be read from the SchemaReferenceType
and SchemaReferenceUri
properties of the JsonSchemaObject class.
In the first part of this work, the concept of JSON Array type was introduced. This section recaps some of the Array parameters discussed previously and introduces new ones.
The schema text shown above should be familiar to the reader by now. It is a schema that describes a JSON Array type that consists of JSON Integer type items. An instance of this Array schema should contain no more than 4 integer items and no less than 1. All items in the Array must be unique.
As shown in the code extracts below, the schema for the array was created using the constructor JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,bool additional_items,string description,bool is_required = false)
JsonSchemaObject _json_schema_integer = new JsonSchemaObject ("integerObject",
JSON_TYPE.INTEGER,null, null, null, string.Empty, false);
JsonSchemaObject _json_schema_array = new JsonSchemaObject ("arrayObject",1,4,true,true,
string.Empty,true);
_json_schema_array.AddObject (_json_schema_integer);
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"arrayExamples","Array Demo Schema", "ExampleID-025");
_json_schema_object.AddObject (_json_schema_array);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
For Example_2_5
, an instance for the schema above is created using the code below.
_instance_object_string_list = new List<JsonInstanceObject> ();
int _array_size = 4;
for (int i=0; i < _array_size; i++)
{
_instance_object_list.Add(new JsonInstanceObject(_json_schema_integer,(i+1)));
}
JsonInstanceObject _json_instance_array = new JsonInstanceObject (_json_schema_array,
_instance_object_list);
_instance_object_list.Clear ();
_instance_object_list.Add (_json_instance_array);
JsonInstanceObject _json_instance_object = new JsonInstanceObject (_json_schema_object,
_instance_object_list);
_instance_string = _json_instance_engine.Serialize (_json_instance_object);
To violate the maximum and minimum number of items set for the Array change the value of the _array_size
variable to a value less than 1 or greater than 4. What did the exception say?
To go against the uniqueItems constraint of the JSON Array, comment out Line 1 and run Example_2_5
. Read the exception thrown.
The JSON Array parameters uniqueItems
, minItems
, maxItems
can be read from the UniqueItems
, MinimumItems
, MaximumItems
properties of the JsonSchemaObject
class.
Below is the serialized instance of the Array schema.
Example_2_5
Counterintuitively, JSON allows Array types to consist of items that are not of the same JSON type. For instance, an Array can contain strings, integers and numbers in the same ordered list. Such an Array is referred to as a tuple. An example of a schema describing a tuple is shown below.
The Array item consists of 1 integer and 3 strings. This example is tuple that describes an address.
The C# code used in creating this tuple is shown below.
JsonSchemaObject _json_schema_house_number = new JsonSchemaObject ("houseNumber",
JSON_TYPE.INTEGER, string.Empty, true);
JsonSchemaObject _json_schema_street_name = new JsonSchemaObject ("streetName", JSON_TYPE.STRING, string.Empty, true);
JsonSchemaObject _json_schema_street_type = new JsonSchemaObject ("streetType",
JSON_TYPE.STRING, string.Empty, true);
_json_schema_street_type .AddEnumList (new List<string>(new string[] {"Street","Avenue","Boulevard"}));
JsonSchemaObject _json_schema_direction = new JsonSchemaObject ("direction", JSON_TYPE.STRING, string.Empty, true);
_json_schema_direction .AddEnumList (new List<string>(new string[] {"NW","NE","SW","SE"}));
JsonSchemaObject _json_schema_array = ew JsonSchemaObject ("arrayObject",null,null,true,true,string.Empty,true);
_json_schema_array.AddObject (_json_schema_house_number);
_json_schema_array.AddObject (_json_schema_street_name);
_json_schema_array.AddObject (_json_schema_street_type);
_json_schema_array.AddObject (_json_schema_direction);
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4, "arrayExamples","Array Tuple Demo Schema", "ExampleID-026");
_json_schema_object.AddObject (_json_schema_array);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
An instance of the schema describing the tuple is done using the code below.
_instance_object_list = new List<JsonInstanceObject> ();
_instance_object_list.Add (new JsonInstanceObject (_json_schema_house_number, 1600));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_street_name, "Pennsylvania"));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_street_type, "Avenue"));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_direction, "NW"));
JsonInstanceObject _json_instance_array = new JsonInstanceObject (_json_schema_array,
_instance_object_list);
_instance_object_list.Clear ();
_instance_object_list.Add (_json_instance_array);
JsonInstanceObject _json_instance_object = new JsonInstanceObject (_json_schema_object,
_instance_object_list);
_instance_string = _json_instance_engine.Serialize (_json_instance_object);
The serialized output of the instance looks like this;
Recall that the constructor used for creating the tuple array was JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,bool additional_items,string description,bool is_required = false). To understand the role played by the additional_items parameter in the constructor, consider that a provision was not made for the address tuple to contain a field for State. However, setting the additional_items parameter to true
allows for extra fields to be added to a tuple during schema instantiation without explicitly defining these extra fields. In Example_2_7
, the State field is added as shown below.
...
_instance_object_list.Add (new JsonInstanceObject (_json_schema_house_number, 1600));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_street_name, "Pennsylvania"));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_street_type, "Avenue"));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_direction, "NW"));
_instance_object_list.Add (new JsonInstanceObject (_json_schema_state, "Washington"));
...
The serialized output is a represented below.
Set the tuple additional_items parameter to false
in Example_2_7
to see if an exception is thrown.
By setting the value of the additional_items parameter to true
any of the 6 JSON types can be added to the tuple. However, instances may arise when additionalItems
attribute must conform to a specific schema. For example, one can restrict the types that can be added to the tuple to JSON String types only. The schema below does exactly that.
Notice that the additionalItems attribute of the array now has a schema as value as opposed to a Boolean value assigned previously.
To set the value of an additional items attribute to a schema use the second array constructor i.e., JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,JsonSchemaObject additional_items,string description,bool is_required = false)
, to instantiate the JSON Array object as is done in Example_2_8
. See the code below.
...
JsonSchemaObject _json_schema_state = new JsonSchemaObject ("state", JSON_TYPE.STRING,
string.Empty, true);
JsonSchemaObject _json_schema_array = new JsonSchemaObject ("arrayObject",null,null,true,
_json_schema_state,string.Empty,true);
...
Note that the default value for the additionalItems attribute is true
. When the additionalItems attribute is not present in a JSON Array schema, it is assumed to be set to true
.
If the additionalItems property is of Boolean type its value can be read from the AdditionalItemsBool
property of the JsonSchemaObject class. If it is of schema type then it should be read from the AdditionalItemsObject
property of the JsonSchemaObject class.
This section presents all attributes of a JSON Object type.
The additionalProperties
attribute is to a JSON Object type schema what additionalItems is to a JSON Array type schema. And like the addItems for JSON Array, the additionalProperties attribute can assume either a Boolean or schema value. The default value for additionalProperties is true
and it is assumed to be set to true
when it is not present as an attribute in a JSON Object schema. In the schema shown below the additionalProperties is assumed to be true.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "ExampleID-029",
"title": "objectExamples",
"description": "OBJECT Additional Properties Demo",
"type": "object",
"properties": {
"jsonObject": {
"type": "object",
"properties": {
"houseNumber": {
"type": "integer"
},
"streetName": {
"type": "string"
},
"streetType": {
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
},
"direction": {
"type": "string",
"enum": ["NW", "NE", "SW", "SE"]
}
},
"required": ["houseNumber", "streetName", "streetType","direction"]
}
},
"required": ["jsonObject"]
}
Notice in the schema presented above that the jsonObject
properties are similar to those of the array tuple i.e., arrayObject
, discussed in the previous section (See the Example_2_8
routine).
The JsonSchemaObject (string object_name,int? minimum_properties,int? maximum_properties,JsonSchemaObject additional_properties,string description,bool is_required = false)
constructor was used to instantiate the jsonObject
as shown in the code below.
JsonSchemaObject _json_schema_house_number = new JsonSchemaObject ("houseNumber",
JSON_TYPE.INTEGER, string.Empty, true);
JsonSchemaObject _json_schema_street_name = new JsonSchemaObject ("streetName", JSON_TYPE.STRING, string.Empty, true);
JsonSchemaObject _json_schema_street_type = new JsonSchemaObject ("streetType",
JSON_TYPE.STRING, string.Empty, true);
_json_schema_street_type .AddEnumList (new List<string>(new string[] {"Street","Avenue","Boulevard"}));
JsonSchemaObject _json_schema_direction = new JsonSchemaObject ("direction", JSON_TYPE.STRING, string.Empty, true);
_json_schema_direction .AddEnumList (new List<string>(new string[] {"NW","NE","SW","SE"}));
_JsonSchemaObject _json_schema_sub_object = new JsonSchemaObject ("jsonObject",null,null,true,string.Empty,true);
_json_schema_sub_object.AddObject (_json_schema_house_number);
_json_schema_sub_object.AddObject (_json_schema_street_name);
_json_schema_sub_object.AddObject (_json_schema_street_type);
_json_schema_sub_object.AddObject (_json_schema_direction);
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"objectExamples","OBJECT Additional Properties Demo", "ExampleID-029");
_json_schema_object.AddObject (_json_schema_sub_object);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
Since the additionalProperties parameter defaults to true
, the state
property can be added to an instance of the schema above as shown in the serialized instance text below.
Set the additionalProperties parameter to false and run the Example_2_9
routine to see the exception thrown.
As was said previously, the additionalProperties attribute can also have a value that is a schema to restrict the JSON types that can be added during schema instantiation. To do this use the JsonSchemaObject (string object_name,int? minimum_properties,int? maximum_properties,JsonSchemaObject additional_properties,string description,bool is_required = false)
constructor. If this constructor is used then the serialized output of the schema should look like the text shown below.
If the JSON Object that is to be instantiated is a top level object use either of the following constructors instead:
JsonSchemaObject (JSON_VERSION version,string title,string description,string object_id,bool additional_properties = true)
or
JsonSchemaObject (JSON_VERSION version,string title,string description,string object_id,JsonSchemaObject additional_properties)
.
The additionalProperties parameter can be read from either the AdditionalPropertiesBool
or AdditionalPropertiesObject
properties of the JsonSchemaObject class, depending on its value type.
Beyond restricting the additional properties value by setting it to a particular schema definition, the JSON schema specification outlines another mechanism called Pattern Properties that gives another level of control on what structure additional properties can take. Pattern Properties is best explained by way of an example using the code fragment below.
/Example_2_13
The sub attributes of the patternProperties
attribute above enforces a rule that says any additional property that has a name that begins with "S_" must be of JSON type String and must have a character length that is not less than 1 and not greater than 255. Correspondingly, any additional property with names beginning with "N_" must be of JSON type NUMBER, must have a value between 1 and 80 and must be a multiple of 5.
The schema above is generated using the code fragment below (See the Example_2_13
routine).
JsonSchemaObject _json_schema_string_specs = new JsonSchemaObject (string.Empty,1,255,
string.Empty,string.Empty, false);
JsonSchemaObject _json_schema_number_specs = new JsonSchemaObject (string.Empty,
JSON_TYPE.NUMBER,1,80,5.0, string.Empty,false);
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4, "objectExamples", "OBJECT Pattern Properties Demo", "ExampleID-0213");
_json_schema_object.AddPatternProperty (_json_schema_string_specs, "S_[a-zA-Z0-9]*");
_json_schema_object.AddPatternProperty (_json_schema_number_specs, "N_[a-zA-Z0-9]*");
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
patternProperties are added to a JSON Object type using the AddPatternProperty (JsonSchemaObject json_schema_object, string property_pattern)
routine as demonstrated above.
An instance of the schema is created using the code fragment below
JsonInstanceObject _json_instance_string = new JsonInstanceObject (new JsonSchemaObject("S_24",JSON_TYPE.STRING,string.Empty), "Donkey Hotey");
JsonInstanceObject _json_instance_number = new JsonInstanceObject (new JsonSchemaObject("N_25",JSON_TYPE.NUMBER,string.Empty),60);
JsonInstanceObject _json_instance_object = new JsonInstanceObject (_json_schema_object,
new List<jsoninstanceobject> (new JsonInstanceObject [] {_json_instance_number, _json_instance_string}));
_instance_string = _json_instance_engine.Serialize (_json_instance_object);
The values of _json_instance_number
and _json_instance_string
objects are consistent with the restrictions placed by the patternProperties definition. Change the value passed to _json_instance_number to 100. Any idea why the exception was thrown?
Below is the serialized textual representation of the instance of the schema.
See the Example_2_14
, Example_2_15 and Example_2_16
routines for more examples on Pattern Properties.
The required
attribute was discussed in Part 1. Formally, it is an ordered list of property names that must be present during instantiation of a schema of an Object type. In this set of classes, if property marked as required is not present during instantiation of a JSON Object schema an exception is thrown.
The minProperties
and maxProperties
govern the lower and upper bound of the number of properties, as it were, that a JSON Object type is allowed to have. In the schema shown below, the jsonObject
attribute is allowed to have between 1 - 6 properties and no more or less.
See the code fragment of Example_2_11
below on how to set the minProperties and maxProperties values.
JsonSchemaObject _json_schema_string = new JsonSchemaObject ("objectString", JSON_TYPE.STRING,
string.Empty, true);
JsonSchemaObject _json_schema_sub_object = new JsonSchemaObject ("jsonObject",1,6,true,
string.Empty,true);
_json_schema_sub_object.AddObject (_json_schema_string);
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4, "objectExamples", "OBJECT Max and Min Properties Demo", "ExampleID-0211");
_json_schema_object.AddObject (_json_schema_sub_object);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
The jsonObject
schema instantiation code fragment below adds four extra items to the object apart from the objectString
property explicitly defined in the schema. This is allowed because the additionalProperties parameter is set to true.
int _extra_properties_count = 4;
for (int i=0; i < _extra_properties_count; i++)
{
_instance_object_list.Add (new JsonInstanceObject (
new JsonSchemaObject ("extraString-" + (i+1).ToString (), JSON_TYPE.STRING,string.Empty,
true), "extra-string_value" + (i+1).ToString ()));
}
JsonInstanceObject _json_instance_sub_object = new JsonInstanceObject (_json_schema_sub_object, _instance_object_list);
_instance_object_list.Clear ();
_instance_object_list.Add (_json_instance_sub_object);
JsonInstanceObject _json_instance_object = new JsonInstanceObject (_json_schema_object,
_instance_object_list);
_instance_string = _json_instance_engine.Serialize (_json_instance_object);
As can be seen in the serialized instance text below, this brings the total number of properties for the schema instance to 5. Change the value of _extra_properties_count
in the Example_2_13
routine to a number greater than or equal to 6 and see if an exception is thrown when the number of properties exceed the maxProperties value.
The minProperties and maxProperties values can be read from the MinimumProperties
and MaximumProperties
properties of the JsonSchemaObject class.
From the schema above notice that the billingObject
has 3 properties i.e., holderName
, creditCard
and billingAddress
. Whilst the billingObject
is required to be present, its 3 properties are not.
Reflect on a scenario where one JSON Object property cannot exist without another. For example, if the creditCard
property is present then the billingAddress
property must also be present and vice-versa. In other words, the creditCard
and the billingAddress
properties are dependent on one another. The JSON schema validation specification can enforce this rule using the dependencies
keyword.
The JSON schema validation specification defines two types of dependencies
: Schema and Property. These two dependencies
types are functionally similar. The code below shows how to implement dependencies
using the set of classes.
JsonSchemaObject _json_schema_name = new JsonSchemaObject ("holderName", JSON_TYPE.STRING,
string.Empty, false);
JsonSchemaObject _json_schema_credit_card= new JsonSchemaObject ("creditCard", JSON_TYPE.NUMBER, string.Empty, false);
JsonSchemaObject _json_schema_billing_address = new JsonSchemaObject ("billingAddress",
JSON_TYPE.STRING, string.Empty, false);
JsonSchemaObject _json_schema_billing_object = new JsonSchemaObject ("billingObject", null,
null,true,string.Empty, true);
_json_schema_billing_object .AddObject(_json_schema_name);
_json_schema_billing_object .AddObject(_json_schema_credit_card);
_json_schema_billing_object .AddObject(_json_schema_billing_address);
List<jsonschemaobject> _schema_dependency_list = new List<jsonschemaobject> ();
_schema_dependency_list.Add (_json_schema_name);
_schema_dependency_list.Add (_json_schema_billing_address);
JsonSchemaObject _json_schema_credit_card_dependency = new JsonSchemaObject(_json_schema_credit_card,_schema_dependency_list);
_schema_dependency_list.Clear ();
_schema_dependency_list.Add (_json_schema_credit_card);
JsonSchemaObject _json_schema_billing_address_dependency =
new JsonSchemaObject(_json_schema_billing_address,_schema_dependency_list );
_json_schema_billing_object.AddDependencies (JSON_DEPENDENCY_TYPE.PROPERTY,
new List<JsonSchemaObject> (new JsonSchemaObject[] {
_json_schema_credit_card_dependency,
_json_schema_billing_address_dependency}));
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"objectExamples","OBJECT Dependencies Properties Demo", "ExampleID-0212");
_json_schema_object.AddObject (_json_schema_billing_object);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
A dependency object is created using the JsonSchemaObject (JsonSchemaObject dependent_object,List<JsonSchemaObject> dependency_object_list)
constructor. In the code lines above, the first parameter for this constructor is the dependent object and the second parameter is the list of objects that this dependent object is dependent on. The _json_schema_credit_card_dependency
schema object above is a dependencies
object that says the creditCard
property is dependent on the holderName
and the billingAddress
properties. The _json_schema_billing_address_dependency
schema object says the billingAddress
property is dependent on the creditCard
property.
Dependencies are added to a JSON Object type using the AddDependencies (JSON_DEPENDENCY_TYPE dependency_type,List<JsonSchemaObject> json_dependency_object_list)
routine. The first parameter passed to this routine is the dependency type required. The second parameter is the list of dependencies to be added to the JSON Object.
When serialized the output string should be similar to what is shown below;
The JSON schema string shown above is an example of a Property dependencies
type.
To get the representation of a Schema dependencies
type shown below, comment out 'Line 1' in the code of Example_2_12
.
Below is an instance of the schema shown above.
{
"billingObject": {
"holderName": "Donkey Hotey",
"creditCard": 234555554444,
"billingAddress": "No. 2 North West Street"
}
}
As a test, exclude the creditCard
property when creating the instance by uncommenting "Line 2" in the Example_2_12
routine to see what exception is thrown.
The interpretation of the schema above says the anonObject
attribute can be of type String, Integer or Boolean during its schema instantiation. The oneOf
keyword gives flexibility to a JSON attribute but with some constraints. Flexibility in that an attribute can be of one JSON type or another and still be valid. And constraint, in the sense that this flexibility is bounded by the types defined within the oneOf
keyword.
Apart from the oneOf constraint, the JSON specification defines three types of constraint i.e, not
, allOf
and anyOf
. See the itemized explanations of these constraints below;
- oneOf : The instance of the schema must match one and only one of the schemas (schemata) defined by the keyword.
not
: The instance of the schema can take any type except for those defined by the keyword.
anyOf
: The instance of the schema can match any one of the schemas (schemata) defined by the keyword.
allOf
: The instance of the schema must match all schemas (schemata) defined by this keyword.
The code fragment used to create and serialize the anonObject schema text above is as shown below.
JsonSchemaObject _json_schema_object_string = new JsonSchemaObject ("stringObject",
JSON_TYPE.STRING,string.Empty);
JsonSchemaObject _json_schema_object_integer = new JsonSchemaObject ("integerObject",
JSON_TYPE.INTEGER ,null, null, null, string.Empty, true);
JsonSchemaObject _json_schema_object_bool = new JsonSchemaObject ("boolObject",
JSON_TYPE.BOOLEAN,string.Empty);
JsonSchemaObject _json_schema_constraint = new JsonSchemaObject ("anonObject",
JSON_CONSTRAINT_TYPE.ONEOF, string.Empty,true);
_json_schema_constraint.AddObject (_json_schema_object_string);
_json_schema_constraint.AddObject (_json_schema_object_integer);
_json_schema_constraint.AddObject (_json_schema_object_bool);
JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4,
"constraintExamples", "OBJECT Constraint Demo", "ExampleID-0217");
_json_schema_object.AddObject (_json_schema_constraint);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);
Observe that the anonObject object is instantiated using this constructor: JsonSchemaObject(string object_name,JSON_CONSTRAINT_TYPE object_constraint,string description,bool is_required = false)
. Notice that the second parameter passed to the constructor is the constraint type i.e., JSON_CONSTRAINT_TYPE.ONEOF
. The schemas (schemata) for the constraints are added using the AddObject (
JsonSchemaObject json_schema_object)
routine as can be seen above.
The anonObject attribute is instantiated using the code below.
JsonInstanceObject _json_instance_anon_object = null;
_json_instance_anon_object = new JsonInstanceObject (new JsonSchemaObject("anonObject",
JSON_TYPE.BOOLEAN,string.Empty),true);
JsonInstanceObject _json_instance_object = new JsonInstanceObject (_json_schema_object,
new List<jsoninstanceobject> (new JsonInstanceObject [] {_json_instance_anon_object}));
_instance_string = _json_instance_engine.Serialize (_json_instance_object);
In the code above, the anonObject
value is set to type Boolean, which of course is one of the three valid schema set in the oneOf
attribute. Comment out the portion marked Line 1 in the Example_2_17
routine and run that example again. Any idea on why that exception is thrown?
The serialized output of the anonObject schema instance is shown below.
For a more involved example of the JSON schema constraint see the Example_2_18
routine.
Same here as in Part 1 ;)
History
01/04/2015: First Version.
02/04/2015: Made changes to the JSON String type schema constructor.
03/05/2016: Modified the DoEscape function of this JSON library to align with the www.json.org escape specification.
03/11/2016: Corrected the JSON de-escape function. Repleaced the attached codes with the Net Core version.