JSON version 4 to C# Objects and Back -- Part 1 - CodeProject

:

Introduction

A means to define a form and have this form downloaded from a database server to a mobile, web or desktop platform was required. This form when filled out is then submitted to the database server.  Two options came to mind on how to go about defining these forms i.e., XML and JavaScript Object Notation (JSON). There was a strong preference for XML at the beginning but the PostgreSQL  jsonb data type which allows for the efficient storage and querying of JSON data tipped it in favour of JSON.

With that settled, the search began for a perfect JSON library in C#. The most complete was the NewtonSoft JSON library. However, it was decided that a library (or set of classes) should be developed in-house instead. The idea was that creating a data instance of the form should fail if the filled out form field is inconsistent with what is defined in the form. With NewtonSoft, one has to first create the data instance and then check if it corresponds with what is defined in the schema. 

The above and the fact that it was an opportunity to learn about JSON was the motivation behind this work.

In Part 1 of this work, the steps required to be effective in creating JSON schemas and instances are presented. In Part 2, more advanced JSON features are discussed. 

Background

In line with the objective stated in the last section, JSON specifies mechanisms by which a form is defined. These mechanisms together are called a JSON Schema object.  Once defined, schemas can be used to guide the creation of JSON data instance objects. To be clear, the JSON Schema is used to define a form and a filled out form is called an Instance of that form.

The JSON schema core specification defines 4 primitive data types (i.e., String, Integer, Number, Boolean and Null) and 2 composite data types (i.e., Array and Object).  A JSON Array object type is an ordered list of zero or more of all or any 6 JSON types. Whilst a JSON Object data type is an unordered list of zero more of all  or any 6 types.

According to the JSON instance specification a mechanism that converts a programming language object to a valid JSON schema or instance string is called a Generator. A Parser is one that converts a valid JSON string into a programming language object.  For this work, a Generator will be called a Serializer and a Parser, a Deserializer.

 

Table of Contents

In the subsections that follow, an attempt is made to demonstrate how to create C# objects that map to all JSON types.  Also presented is how to serialize C# objects to JSON schemas and instance strings, as well as how to deserialize JSON strings back to C# objects. This section will conclude on how to find JSON objects by name and how characters are escaped and unescaped during serialization and deserialization.

The demonstration of the many mechanisms of these set of JSON C# classes is presented by way of examples. The presented examples assume that a job employee applicant form is to be created and filled out. 

Ideally, all related components of a JSON schema should be contained within a top level schema object. The top-level object describes the form, gives it a title, an identity but most importantly informs on what version of the JSON schema standard the form conforms to. Note that this work is based on version 4 of the JSON schema specification.

//Example 1_1

JsonSchemaEngine _json_schema_engine   =  new JsonSchemaEngine(); 


JsonSchemaObject _json_schema_person_details = new JsonSchemaObject (JSON_VERSION.V4, 
"PersonInfo","Prospective Employee Detail Form", "FormID-001");

_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

The code to create a top level JSON schema object is shown above. As can be seen, the schema object constructor, JsonSchemaObject (JSON_VERSION version,string title,string description,string object_id,bool additional_properties = true),  is passed the version, title, description and identity of the form. For now, ignore the additional_properties parameter as it is treated in Part 2 of this work.


The JsonSchemaEngine object is instantiated and used to serialize the top level object. The contents of the _schema_string is shown below.

//output of Example_1_1

/*
{
  "$schema": "http://json-schema.org/draft-04/schema#", 
  "type": "object",
  "id": "FormID-001",
  "title": "PersonInfo",
  "description": "Prospective Employee Detail Form"
}

*/

The $schema attribute in the JSON schema above indicates that the form conforms to the version 4 draft of the schema specification. The type attribute says the top level object is of JSON type Object and as a result can contain other JSON types. The remaining attributes are self explanatory.

The top level C# schema's object version, type, id, title and description can be read from the following JsonSchemaObject  Version, ObjectType, ObjectID, Title and Description properties, respectively.

The code for the above can be found in the Example_1_1 funtion of the attached solution.

Let the first two fields of the form be the first and last name of the applicant. These fields are of JSON String types and must be between 2 - 100 characters long each. Both of these fields are mandatory. The code to create these fields is shown below;

// Example_1_2

...

JsonSchemaObject _json_schema_first_name = new JsonSchemaObject ("firstName", 2, 100, 
string.Empty, "First Name of Applicant", true);

JsonSchemaObject _json_schema_last_name = new JsonSchemaObject ("lastName", 2, 100,string.Empty, "Last Name of Applicant", true);

_json_schema_person_details.AddObject (_json_schema_first_name);
_json_schema_person_details.AddObject (_json_schema_last_name);

_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

JSON String types for the schema can be created using the following JsonSchemaObject constructor; JsonSchemaObject (string object_name,int? minimum_length, int? maximum_length,string pattern,string description,bool is_required = false)

When the schema objects of these fields are created they are added to the top level object  i.e, _json_schema_person_details using the  JsonSchemaObject AddObject (JsonSchemaObject json_schema_object)  instance routine, as shown above .

The serialized string of the top level object is shown below;

//Example_1_2

/*
{
    "$schema": "http://json-schema.org/draft-04/schema#", 
    "type": "object",
    "id": "FormID-001",
    "title": "PersonInfo",
    "description": "Prospective Employee Detail Form",
    "properties":  {
                    
                    "firstName": {
                    "description": "First Name of Applicant",
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 100
                    },

                    "lasttName": {
                    "description": "Last Name of Applicant",
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 100
                   }
    },

  "required": ["firstName", "lasttName"]
}
*/

When other JSON types are added to a JSON Object type, these added types are called properties of the Object. That is the reason why the first and last name fields are within the curly braces tagged properties. Observe that towards the end of the string above, the firstName and lastName objects are tagged as required. This is so as the is_required parameters was set to true for both objects. The fields do not need to conform to any string pattern so the string pattern attribute was set to string.Empty. An example is given later in this work that shows how the pattern property can be set. 

The following JsonSchemaObject properties are only associated with a C# object of JSON type String; MaximumLength, MinimumLength and Pattern. The name of JsonSchemaObject object can read from the ObjectName property.

The code fragment below shows how to create an instance of the form as it currently stands.

//Example_1_2

JsonInstanceObject _json_instance_first_name = new JsonInstanceObject (_json_schema_first_name,"Donkey");

JsonInstanceObject _json_instance_last_name  = new JsonInstanceObject (_json_schema_last_name,
"Hotey");

_instance_object_string_list = new List<JsonInstanceObject> ();
_instance_object_string_list.Add (_json_instance_first_name);
_instance_object_string_list.Add (_json_instance_last_name);


JsonInstanceObject _json_instance_person_details = new JsonInstanceObject (_json_schema_person_details,_instance_object_string_list);

_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);

Creating an instance of the JsonSchemaObject involves calling the JsonInstanceObject (JsonSchemaObject json_schema_object, string json_instance_value) constructor and passing it both the defined schema as well as the value of the instance as shown above.

If the string value passed as the first or last name violates the minimum or maximum character length then an exception is thrown immediately and the instance is not created.

As soon as these first and last name instance objects are created, they are passed as a list to the instance object of the _json_schema_person_details schema object using the following constructor JsonInstanceObject (JsonSchemaObject json_schema_object, List<JsonInstanceObject> json_instance_object_list).

Notice the _json_schema_person_details  schema object is also passed to _json_instance_person_details along with the instance object list. This ensures that the resulting instance of the form conforms to the parameters set in the schema of the form. For instance, an exception is thrown if the firstName or lastName objects are not included in the instance list. Recall that both of these attributes are required.  Amongst other checks, one is carried out to ensure that both the firstName or lastName objects are of JSON type String as defined in the schema. The serializing of a JSON instance object follows a similar process as the serialization of a JSON schema. The serialized instance string output is shown below;

//Example_1_2

/*
{
  "firstName": "Donkey", 
  "lastName": "Hotey"
}
*/

If a JSON instance is of type string, then the instance value is gotten by reading the ObjectStringValue property of the JsonInstanceObject class. The name of JSON instance C# object is stored in the ObjectName property of the JsonInstanceObject class.

See the Example_1_2 routine for the complete code.

JSON defines an array enum keyword which helps to restrict the range of values a JSON instance object can assume. For instance, the form can restrict the applicant's gender to one of five choices. The steps for implementing this constraint is shown below.

// Example_1_3
...

JsonSchemaObject _json_schema_gender = new JsonSchemaObject ("gender", null, null, string.Empty, "Gender of Applicant", true);

 List<string> _gender_options = new List<string> ();
_gender_options.Add("male");
_gender_options.Add("female");
_gender_options.Add("transgender");
_gender_options.Add("intersex");
_gender_options.Add("other");
_json_schema_gender.AddEnumList (_gender_options,1);

_json_schema_person_details.AddObject (_json_schema_gender);

_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

It can be seen that a gender object of type JSON String is created and a list of possible values of the gender attribute is attached to this object using the JsonSchemaObject AddEnumList (object object_enum,int default_index = -1) routine. 

The output for the serialized _json_schema_person_detail is shown below.

//Example_1_3

/*
{
...
"properties":  {
 
...
   "gender": {
           "description": "Gender of Applicant",
            "type": "string",

           "enum": ["male", "female", "transgender", "intersex", "other"],
           "default": "female"
          }
  },
  "required": ["firstName", "lastName", "gender"]
}
*/

Notice that the default value for the gender object is set to female. This is so because an integer value of 1 was passed as the second parameter for the AddEnumList routine. 1 is the index of the string "female" in the _gender_options list.  If a default value is not required then that argument should be left out or set to -1. A default value can also be set independently of the AddEnumList routine by using the SetDefaultValue (object default_value) routine.  See Example_1_14 for use cases on how to set default values for all 6 JSON types using this routine.

Note that all objects in the list passed to the AddEnumList routine must be unique, otherwise an exception is thrown.

//Example_1_3
...

JsonInstanceObject _json_instance_gender = new JsonInstanceObject (_json_schema_gender,"other");

...

_instance_object_string_list.Add (_json_instance_gender);

_json_instance_person_details = new JsonInstanceObject (_json_schema_person_details,_instance_object_string_list);

 
_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);

The code above shows how to create an instance of the  _json_schema_gender  object. Once created, this gender instance is added to the _json_instance_person_details object and then serialized to give the string output below. 

Example_1_3

/*
{
  "firstName": "Donkey", 
  "lasttName": "Hotey", 
  "gender": "other"
}
*/

If the _json_instance_gender is passed a string value other than those in the enum array an exception is thrown. On the other hand, if an empty or null string is passed as its value, its value becomes the default value i.e., "female". The code for the above is contained in Example_1_3 routine.

All 6 JSON types can be assigned enum array and default values. For JSON types Number, Integer and Boolean by passing the AddEnumList routine a C#  double?, int? and bool?  lists, respectively.
For JSON Object and Array Types, on the other hand, pass a list of JsonInstanceObject. Codes demonstrating how to do the above is given in Example_1_8 and Example_1_9 in the attached solution file.

The Enum list is contained in the Object list of the EnumInstanceObject property of the JsonSchemaObject class. The default value can be read from the DefaultInstanceObject property of the same class.

JSON Schema Integer and Number Types

In this work, the JSON Integer type is mapped to C# int? and the Number type to double?. Apart from this, the JSON Integer and Number types have the same set of object properties and are manipulated in a similar way. With this in mind, assume that an age field which is of JSON type Integer is required in the form. Also assume that the value for this field must be between 18 and 55. The constructor, JsonSchemaObject (string object_name,JSON_TYPE json_type, double? minimum_value,double? maximum_value, double? multiple_of_value, string description,bool is_required = false), is used to create this field thus;

//Example_1_4

JsonSchemaObject _json_schema_age = new JsonSchemaObject ("age", JSON_TYPE.INTEGER, 18,55, null, "The Age of the Applicant ", true);

...

_json_schema_person_details.AddObject (_json_schema_age);

_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

This constructor will only accept JSON_TYPE.NUMBER and JSON_TYPE.INTEGER as its second parameter, otherwise and exception is thrown. The multiple_of_value parameter is set to null as it is not required. But if the form wants to restrict the instance value of _json_schema_age object to a multiple of 5, then that parameter should be set to 5. In this case, assigning a value other than those that are multiples of this parameter value causes an exception to be thrown.
The serialized string for the top level object should now have an attribute called age as shown below:

//Example_1_4

/*
{
...
"properties":  {
...
        "age": {
              "description": "The Age of the Applicant ",
              "type": "integer",
              "minimum": 18,
              "maximum": 55
               }
        },
"required": ["firstName", "lastName", "gender", "age"]
}
*/

The JsonSchemaObject properties that are unique to a JSON Integer or Number type are as follows: MaximumValue, MinimumValue and MultipleOfValue. There are two other properties that are associated with a JSON Integer or Number type i.e., ExclusiveMinimum and ExclusiveMaximum. Setting the ExclusiveMinimum property to true means that a value passed to the instance of the _json_schema_age object must exclude the value 18 i.e., valid values start from 19. Analogously, if the ExclusiveMaximum property is set to true then the highest valid value for the _json_schema_age instance is 54. The default values for ExclusiveMinimum and ExclusiveMaximum properties is false.

An instance of the age schema object is created using the code snippet following immediately. 

//Example_1_4
...

JsonInstanceObject _json_instance_age = new JsonInstanceObject (_json_schema_age,28);

...

_instance_object_string_list.Add (_json_instance_age);

_json_instance_person_details = new JsonInstanceObject (_json_schema_person_details,_instance_object_string_list);

_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);

If the value passed to the JsonInstanceObject (JsonSchemaObject json_schema_object, int? json_instance_value) constructor is less than 18 or greater than 55 an exception is thrown. If the _json_schema_age object was of type Number then this constructor JsonInstanceObject (JsonSchemaObject json_schema_object,double? json_instance_value) is called instead. 

If the JsonInstanceObject type is Number then its associated value can be gotten from the instance class ObjectDoubleValue property. If it is of type Integer then its value can be read from the ObjectIntegerValue property of the class.

Serializing  an instance of the form should produce the following output;

 

//Example_1_4

/*
{
    "firstName": "Donkey", 
    "lastName": "Hotey", 
    "gender": "other", 
    "age": 28
}
*/

See the Example_1_4 routine in the attached solution for the full code listing.

JSON Schema Boolean Type

To describe how a JSON Boolean type is implemented in this work assume that the form has a compulsory field in which an applicant must say if further training is required. The valid values for this field should be either true or false. The code below shows how the above can be accomplished.

//Example_1_5
...

JsonSchemaObject _json_schema_requires_training = 
	new JsonSchemaObject ("training?", JSON_TYPE.BOOLEAN, "Does the applicant require training?", true);

List<bool?> _training_options = new List<bool?> ();

_training_options.Add(true);
_training_options.Add(false);

_json_schema_requires_training.AddEnumList (_training_options);


....

_json_schema_person_details.AddObject (_json_scon_schema_requires_training) 

_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

In this work JSON Boolean types map to C# bool?.

The serialize form should now have a required attribute called "training?" as shown below. 

// Example_1_5

/*
{
    ...
   "properties":  {

    ...

        "training?": {
             "description": "Does the applicant require training?",
            "type": "boolean",
            "enum": [true, false]
               },   

  "required": ["firstName", "lastName", "gender", "age", "training?"]
}


*/

Notice that no default value is set for this attribute. Can you guess the reason why?

// Example_1_5
  
JsonInstanceObject _json_instance_requires_training = 
new JsonInstanceObject (_json_schema_requires_training,false);

...

_innstance_object_list.Add (_json_instance_requires_training);

JsonInstanceObject _json_instance_person_details = new JsonInstanceObject (_json_schema_person_details,_instance_object_list);
			
_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);

An instance of the _json_schema_requires_training object is created using the following constructor JsonInstanceObject (JsonSchemaObject json_schema_object, bool? json_instance_value) (see code snippet above). The value associated with an instance of a JSON Boolean type can be retrieved from the ObjectBooleanValue property of the JsonInstanceObject. 

The serialized instance of the form should now include the training attribute as shown below.

//Example_1_5

/*

{
   "firstName": "Donkey", 
   "lastName": "Hotey", 
   "gender": "other", 
   "age": 28, 
   "training?": false
}

*/

See the Example_1_5 routine for the complete code.

JSON Schema Object Type

Consider an applicant's address field in the form that consists of four sub fields i.e., House Number, Street, City and State. This field can adequately be represented as a JSON schema Object type with four properties i.e., 1 JSON Integer and 3 JSON String types.  The lines of code for creating the address object is shown below;

//Example_1_6
...

JsonSchemaObject _json_schema_address = new JsonSchemaObject ("address", JSON_TYPE.OBJECT,"The address of the applicant", true);

JsonSchemaObject _json_schema_house_number = new JsonSchemaObject ("houseNumber", 
JSON_TYPE.INTEGER, string.Empty, true);

JsonSchemaObject _json_schema_street = new JsonSchemaObject ("street", JSON_TYPE.STRING, 
string.Empty, true);

JsonSchemaObject _json_schema_city   = new JsonSchemaObject ("city", JSON_TYPE.STRING,
 string.Empty, true);

JsonSchemaObject _json_schema_state = new JsonSchemaObject ("state", JSON_TYPE.STRING, string.Empty, true);


_json_schema_address.AddObject (_json_schema_house_number);
_json_schema_address.AddObject (_json_schema_street);
_json_schema_address.AddObject (_json_schema_city);
_json_schema_address.AddObject (_json_schema_state);

...

_json_schema_person_details.AddObject (_json_schema_address);
			
_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

Notice that the four properties of the address object use the shorthand constructor i.e., JsonSchemaObject (string object_name,JSON_TYPE json_type,string description,bool is_required = false) for instantiation. This constructor allows for the creation of a JSON schema object with all other relevant schema parameters set to their default values. The address object itself uses this shorthand constructor. The full constructor for creating a JSON Object type along with the associated JsonSchemaObject properties are discussed in Part 2

The serialized output of the top level object should have an address attribute which looks like this;

//Example_1_6

/*
{
    ...
   "properties":  {

    ...

        "address": {
                   "description": "The address of the applicant",
                   "type": "object",
                   "properties":  {
                                         "houseNumber": {
                                         "type": "integer"
                                          },

                                         "street": {
                                         "type": "string"
                                          },

                                          "city": {
                                          "type": "string"
                                          },

                                         "state": {
                                         "type": "string"
                                          }
                                        },

        "required": ["houseNumber", "street", "city", "state"]
             }
          },

   "required": ["firstName", "lastName", "gender", "age", "training?", "address"]
}


*/

The reader would have noticed that this is an example of having one JSON Object type (i.e., address object) as a property of another JSON Object type (i.e., top level object).

Using the  JsonInstanceObject (JsonSchemaObject json_schema_object, List<JsonInstanceObject> json_instance_object_list) constructor,  an instance of the _json_schema_address object is created like so;

//Example_1_6
...

JsonInstanceObject _json_instance_house_number = 
new JsonInstanceObject (_json_schema_house_number,345);

JsonInstanceObject _json_instance_street = 
new JsonInstanceObject (_json_schema_street ,"North-West");

JsonInstanceObject _json_instance_city = new JsonInstanceObject (_json_schema_city ,"New York");
JsonInstanceObject _json_instance_state = new JsonInstanceObject (_json_schema_state ,
"New York");


_instance_object_list = new List<jsoninstanceobject> ();

_instance_object_list.Add (_json_instance_house_number);
_instance_object_list.Add (_json_instance_street);
_instance_object_list.Add (_json_instance_city);
_instance_object_list.Add (_json_instance_state);


JsonInstanceObject _json_instance_address = 
new JsonInstanceObject (_json_schema_address ,_instance_object_list);


_instance_object_list.Clear ();

...

_instance_object_list.Add (_json_instance_address);

JsonInstanceObject _json_instance_person_details = new 
JsonInstanceObject (_json_schema_person_details,_instance_object_list);
			
_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);

The objects that make up an instance of a JSON Object type are contained in the  ObjectList property JsonInstanceObject class.

The serialized output of the instance of the form should now look like this;

//Example_1_6

/*

{
    "firstName": "Donkey", 
    "lastName": "Hotey", 
    "gender": "other", 
    "age": 28, 
    "training?": false, 
    "address":  {
               "houseNumber": 345, 
               "street": "North-West", 
               "city": "New York", 
               "state": "New York"
    }
}
  

*/

JSON Schema  Array Type

Let the form mandate that the applicant should include contact phone numbers. Each number must have an associated phone number type designation e.g., Home, Work etc. Furthermore, the applicant's phone number digits must begin with a 0 and must be 11 digits long. The applicant is also allowed to include a minimum of one number and a maximum of three. 

Based on the requirements above, a contact phone number is a 2-tuple object i.e., phone number type and phone number digits.  This can be accommodated as a JSON Object type with two JSON types String properties.
Allowing an applicant to include 1 to 3 phone number objects can be achieved using a JSON Array Type.
The code below shows how this might be approached. 

 //Example_1_7
...
   
JsonSchemaObject _json_schema_number_type = new JsonSchemaObject ("numberType", 
JSON_TYPE.STRING, string.Empty, true);

JsonSchemaObject _json_schema_number     = new JsonSchemaObject ("number", null,null,"0\\d{10}", string.Empty, true);

JsonSchemaObject _json_schema_number_object = new JsonSchemaObject (string.Empty, 
JSON_TYPE.OBJECT,string.Empty, true);

_json_schema_number_object.AddObject (_json_schema_number_type);
_json_schema_number_object.AddObject (_json_schema_number);


JsonSchemaObject _json_schema_number_array = new JsonSchemaObject ("phoneNumber",1,3,true,false,"Phone Numbers of Applicant",true);

_json_schema_number_array.AddObject (_json_schema_number_object);

...

_json_schema_person_details.AddObject (_json_schema_number_array);	
		
			
_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);

Notice that the pattern parameter for the _json_schema_number object is set to RegEx string of "0\\d{10}". This is consistent with the requirement set for the digits of the phone number. 
The JSON Array type object is created using the following JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,bool additional_items,string description,bool is_required = false)  constructor.

As per the requirements the minimum_items and maximum_items parameters of the constructor are set to 1 and 3, respectively. If an applicant enters less than 1 or more than three numbers during instantiation of the form and exception is immediately thrown. The is_unique_items parameter is set to true that ensure that all number objects entered are unique. An exception is thrown if two instances of the number objects have the same phone number type and phone number digits. The additional_items parameter is covered in Part 2. The properties associated with a JsonSchemaObject for a JSON array type are also discussed in Part 2.

The serialized output for the phoneNumber object is shown below;

//Example_1_7

/*
{
    ...
   "properties":  {

    ...

       "phoneNumber": {
                      "description": "Phone Numbers of Applicant",
                      "type": "array",
                      "items": 
                              {
                                "type": "object",
                                 "properties":  {
                                              "numberType": {
                                              "type": "string"
                                               },

                                               "number": {
                                               "type": "string",
                                               "pattern": "^0\d{10}$"
                                               }
                                            },
                                 "required": ["numberType", "number"]
                              },
                      "minItems": 1,
                      "maxItems": 3,
                      "uniqueItems": true,
                      "additionalItems": false
                     }
                  },
"required": ["firstName", "lastName", "gender", "age", "training?", "address", "phoneNumber"]
}


*/

The output shown above is an example of JSON Object type (i.e., _json_schema_number_object) inside a JSON Array type (i.e., _json_schema_number_array) and JSON Array type (i.e., _json_schema_number_array) inside a JSON Object type (i.e., _json_schema_person_details). This demonstrates three levels of JSON Object nesting.

An instance of the _json_schema_number_array is created using the JsonInstanceObject (JsonSchemaObject json_schema_object, List<JsonInstanceObject> json_instance_object_list) constructor as shown below.

//Example_1_7
...

//create first phone number instance object

JsonInstanceObject _json_instance_type = 
new JsonInstanceObject(_json_schema_number_type,"Home");


JsonInstanceObject _json_instance_number = new JsonInstanceObject(_json_schema_number,
"07843487433");

_instance_object_list = new List<jsoninstanceobject> ();
_instance_object_list.Add (_json_instance_type);
_instance_object_list.Add (_json_instance_number );


JsonInstanceObject _json_instance_number_object_1 = 
new JsonInstanceObject(_json_schema_number_object,_instance_object_string_list);


//create second phone number instance object

_instance_object_list.Clear ();

_json_instance_type = new JsonInstanceObject(_json_schema_number_type,"Work");
_json_instance_number = new JsonInstanceObject(_json_schema_number,"06890487422");

_instance_object_list = new List<jsoninstanceobject> ();
_instance_object_list.Add (_json_instance_type);
_instance_object_list.Add (_json_instance_number );

			

JsonInstanceObject _json_instance_number_object_2 = new JsonInstanceObject(_json_schema_number_object,_instance_object_list);



//create phone number instance Array


_instance_object_string_list.Clear ();
_instance_object_string_list.Add (_json_instance_number_object_1);
_instance_object_string_list.Add (_json_instance_number_object_2);


JsonInstanceObject _json_instance_number_array = 
new JsonInstanceObject(_json_schema_number_array,_instance_object_list);

_instance_object_list.Clear ();

...

_instance_object_string_list.Add (_json_instance_number_array);

_json_instance_person_details = new JsonInstanceObject (_json_schema_person_details,_instance_object_string_list);

_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);

The objects that make up an instance of a JSON Array type can be found in the  ObjectList property JsonInstanceObject class.

The serialized output for the form instance is shown below;

//Example_1_7

/*
{
    "firstName": "Donkey", 
    "lastName": "Hotey", 
    "gender": "other", 
    "age": 28, 
    "training?": false, 
    "address":  {
               "houseNumber": 345, 
               "street": "North-West", 
               "city": "New York", 
               "state": "New York"
               },

   "phoneNumber": [
                   {
                    "numberType": "Home", 
                    "number": "07843487433"
                   },
 
                   {
                    "numberType": "Work", 
                    "number": "06890487422"
                   }
              ]
}
*/

All related codes for the above can be found in the Example_1_7 routine.

In order to show how the JSON Null type is implemented in this work allow for a field called spouse to be included in the form. Also allow for the fact that this field is not to be used in the current version of the form but maybe needed in the future. One way of the many ways to ensure that this field is present in the form is to designate it as a JSON Null type. 

//Example_1_10
...
    
JsonSchemaObject _json_schema_spouse = new JsonSchemaObject ("spouse", JSON_TYPE.NULL, 
"To be used in the future", true);

_json_schema_person_details.AddObject (_json_schema_spouse);

_schema_string = _json_schema_engine.Serialize (_json_schema_person_details);  

Serializing the code above will include the spouse attribute as one of the properties of the top level JSON object as shown below;

//Example_1_10

/*       
 {

     ...

     "properties":  {

              ...

              "spouse": {
                    "description": "To be used in the future",
                    "type": "null"
                 }

     },

    "required": ["firstName", "lastName", "gender", "age", "training?", "address",
    "phoneNumber", "spouse"]
}
    
 */ 

An instance of the _json_schema_spouse object is created using the JsonInstanceObject (JsonSchemaObject json_schema_object) constructor as shown below;

//Example_1_10
...  

JsonInstanceObject _json_instance_spouse = new JsonInstanceObject(_json_schema_spouse);

_instance_object_list.Add (_json_instance_spouse);

_instance_string  = _json_instance_engine.Serialize (_json_instance_person_details);   

What the final version of an instance of the form will look like when serialized is as depicted below;

 // Example_1_10       
 /*

{
    "firstName": "Donkey", 
    "lastName": "Hotey", 
    "gender": "other", 
    "age": 28, 
    "training?": false, 
    "address":  {
               "houseNumber": 345, 
               "street": "North-West", 
               "city": "New York", 
               "state": "New York"
               },

   "phoneNumber": [
                   {
                    "numberType": "Home", 
                    "number": "07843487433"
                   },
 
                   {
                    "numberType": "Work", 
                    "number": "06890487422"
                   }
              ],
              
    "spouse": null

}
*/  

See the Example_1_10 routine for the complete code.

Converting or deserializing a valid JSON schema string is similar to the serialization process.
After the JsonSchemaEngine is instantiated, a valid JSON schema string is passed to the Deserialize (string json_schema) routine which returns JsonSchemaObject C# top level object. 

Note that a limit can be placed on the size in bytes of a JSON schema string that can be deserialized by calling the JsonSchemaEngine(int schema_size_byte_limit = -1) constructor instead. An exception is thrown if the size of the json_schema parameter passed to the Deserialize routine is larger than the value specified for the schema_size_byte_limit when the JsonSchemaEngine is being instantiated. 

//Example_1_11 
....

//Deserializing a JSON Schema

JsonSchemaEngine _json_schema_engine   =  new JsonSchemaEngine(); 

/*JsonSchemaEngine _json_schema_engine   =  new JsonSchemaEngine(500);*/


JsonSchemaObject _json_schema_person_details_2  =  
_json_schema_engine.Deserialize (_schema_string);




//Deserializing a JSON schema instance

JsonInstanceEngine 	_json_instance_engine = new JsonInstanceEngine();

/*JsonInstanceEngine 	_json_instance_engine = new JsonInstanceEngine(500);*/

JsonInstanceObject  _json_instance_person_details_2 = 
_json_instance_engine.Deserialize (_json_schema_person_details, _instance_string);   

Deserializing an instance of a JSON schema requires that the schema used to create that instance is present and passed to the JsonInstanceObject Deserialize (JsonSchemaObject json_schema_object,string json_instance) routine of the JsonInstanceEngine class as shown above.

As can be seen above, a limit can also be placed on the size of the JSON instance string that can be deserialized by setting the instance_size_byte_limit parameter in the JsonInstanceEngine (int instance_size_byte_limit = -1) constructor to the specified maximum size.  

See the Example_1_11 routine.

A component object of a JSON C# schema object can be retrieved and read by calling the JsonSchemaObject static FindSchemaObjectByName (string json_schema_name, JsonSchemaObject json_schema_object) routine and passing it the name of the object to be read, as well as the container object. In the example given below, an attempt is made to retrieve the gender schema object from the _json_schema_person_details object using this routine. If the gender object is returned, its associated properties can then be read as shown below. 

//Example_1_12

JsonSchemaObject _json_schema_object = 
JsonSchemaObject.FindSchemaObjectByName ("gender",_json_schema_person_details);

if (_json_schema_object != null)
{
			
	Console.WriteLine ("Object Name: {0}", _json_schema_object.ObjectName);

	Console.WriteLine ("JSON Type: {0}",  _json_schema_object.ObjectType);

	Console.WriteLine ("Minimum Length: {0}",_json_schema_object.MinimumLength);

	Console.WriteLine ("Maximum Length: {0}",_json_schema_object.MaximumLength);

	Console.WriteLine ("Required?: {0}", _json_schema_object.IsRequired);

	Console.WriteLine ("Default Value: {0}", 
    _json_schema_object.DefaultInstanceObject.ObjectStringValue);

	Console.WriteLine ("---------------------enum items---------------------------");
				foreach (JsonInstanceObject enum_item in 
                            _json_schema_object.EnumInstanceObject.ObjectList)

					Console.WriteLine ("\t\t {0}",enum_item.ObjectStringValue);
				
}

A representation of the output to the console is given below;

//Example_1_12 
   
/*

Object Name: gender
JSON Type: STRING
Minimum Length: 
Maximum Length: 
Required?: True
Default Value: female
---------------------enum items---------------------------
		 male
		 female
		 transgender
		 intersex
		 other


*/

 

An analogue for the FindSchemaObjectByName routine for the JsonSchemaObject class also exists for the JsonInstanceObject  class i.e., FindInstanceObjectByName (string object_name, JsonInstanceObject json_instance_object). Excerpts from the Example_1_12 routine shown below demonstrates its use.

  // Example_1_12

JsonInstanceObject _json_instance_object = 
JsonInstanceObject.FindInstanceObjectByName ("gender",_json_instance_person_details);

  if (_json_instance_object != null) 
  {
				
	Console.WriteLine ("Object Name: {0}", _json_instance_object.ObjectName);
	Console.WriteLine ("JSON Type: {0}",   _json_instance_object.ObjectType);
	Console.WriteLine ("Object Value: {0}",   _json_instance_object.ObjectStringValue);
			
}
//Example_1_12
 /*
  
 Object Name: gender
 JSON Type: STRING
 Object Value: other
 
 */

 

The JsonSchemaEngine, during the serialization process will attempt to escape certain special JSON characters  e.g. the double quote ' " ', when they appear within the body of a text. See below for a scenario when this happens. These escaped strings are unescaped during the schema deserialization process.

//Example_1_13

JsonSchemaObject _json_schema_object = new JsonSchemaObject (JsonUtilities.JSON_VERSION.V4, 
"xterEscapeExamples", "Character escape Demo", "ExampleID-0113");

JsonSchemaObject _json_schema_string  = new JsonSchemaObject ("Quote\"name\"",JSON_TYPE.STRING,
"Testing Xter Escape");

_json_schema_object.AddObject (_json_schema_string);
_schema_string = _json_schema_engine.Serialize (_json_schema_object);   
//Example_1_13
/*       
{
  "$schema": "http://json-schema.org/draft-04/schema#",

  "id": "ExampleID-0113",

  "title": "xterEscapeExamples",

  "description": "Character escape Demo",

  "type": "object",

  "properties":  {

       "Quote \"name\"": {

           "description": "Testing Xter Escape",
           "type": "string"
       }
  }
}
*/  

The same is true JsonInstanceEngine as shown below;

 //Example_1_13
       
 JsonInstanceObject  _json_instance_string =  new JsonInstanceObject (_json_schema_string, "C is "quirky", "flawed", and an enormous success.---- Dennis M. Ritchie" );

JsonInstanceObject _json_instance_object = new JsonInstanceObject (_json_schema_object,
new List<JsonInstanceObject> (new JsonInstanceObject []    {_json_instance_string}));


_instance_string = _json_instance_engine.Serialize (_json_instance_object);    
Example_1_13
/*        
{
"Quote": "C is \"quirky\", \"flawed\", and an enormous success.---- Dennis M. Ritchie"
}
*/      

Points of Interest

Nothing interesting to report, I am afraid ;)

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.