JSON 版本 4 到 C# 对象和反向 - 第 2 部分






4.94/5 (23投票s)
一套用于创建和操作 JSON Schema 和 Instances 的 C# 类。
引言
假定读者已经阅读并理解了本系列两部分文章的第一部分。如果已阅读,请继续阅读本文。
目录
使用代码
与第一部分一样,使用代码片段描述 JSON 概念及其实现方式。这里使用的一些示例基于 Michael Droettboom 和 Space Telescope Science Institute 的其他人工作。
JSON 引用
//Example_2_1
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-021",
"title": "refExamples",
"description": "REF Demo Schema",
"type": "object",
"properties": {
"integerOne": {
"type": "integer",
"minimum": 200,
"maximum": 500,
"exclusiveMinimum": true,
"exclusiveMaximum": true,
"multipleOf": 20
},
"integerTwo": {
"type": "integer",
"minimum": 200,
"maximum": 500,
"exclusiveMinimum": true,
"exclusiveMaximum": true,
"multipleOf": 20
},
"integerThree": {
"type": "integer",
"minimum": 200,
"maximum": 500,
"exclusiveMinimum": true,
"exclusiveMaximum": true,
"multipleOf": 20
}
},
"required": ["integerOne", "integerTwo", "integerThree"]
}
*/
考虑上面的 JSON schema 文本。请注意,顶层 schema 的三个属性,即 integerOne
、integerTwo
和 integerThree
,都具有相同的参数。尽管如此,每个属性都必须单独创建,从而违反了DRY 原则。JSON schema 规范试图通过三种机制来缓解这种定义重复。这些机制允许在对象外部定义对象参数,然后在对象内部引用这些定义的参数。 在运行时,这些引用将被引用的参数替换。
对于这项工作,三种引用技术分别称为内部、定义和外部。
内部引用
//Example_2_2
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-022",
"title": "refExamples",
"description": "REF Demo Schema",
"type": "object",
"properties": {
"integerOne": {
"id": "integer_id_001",
"type": "integer",
"minimum": 200,
"maximum": 500,
"exclusiveMinimum": true,
"exclusiveMaximum": true,
"multipleOf": 20
},
"integerTwo": {
"$ref": "#integer_id_001"
},
"integerThree": {
"$ref": "#integer_id_001"
}
},
"required": ["integerOne", "integerTwo", "integerThree"]
}
*/
上面是实现内部引用的 schema 示例。如您所见,参数在 integerOne 属性中定义一次,然后使用带有 IntegerOne
标识的 $ref
关键字的引用放置在另外两个属性中。 在功能上,上述 schema 文本与 Example_2_1
例程中的 schema 文本相似。要检查这一点,请反序列化上面的 JSON 文本,然后再次序列化它。打印序列化后的文本以确认这一点。
下面是用于生成上述 schema 文本的 C# 代码。
//Example_2_2
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);
请注意,integerTwo 和 IntegerThree 是使用引用构造函数实例化的,即 JsonSchemaObject (string object_name,JSON_POINTER_TYPE ref_type,string json_schema_reference_uri,string description,bool is_required = false)
。 除了对象名称外,此构造函数还接收引用的类型,在本例中为 JSON_POINTER_TYPE.INTERNAL
,以及被引用对象的标识,即 _json_schema_integerOne.ObjectID
。
如果在反序列化过程中引用的对象找不到,则会抛出异常。
引用类型及其关联的 URI 可以从 JsonSchemaObject
类的 SchemaReferenceType
和 SchemaReferenceUri
属性中读取。
//Example_2_3
/* {
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-023",
"title": "refExamples",
"description": "REF Demo Schema",
"type": "object",
"properties": {
"integerOne": {
"$ref": "#/definitions/integerDef"
},
"integerTwo": {
"$ref": "#/definitions/integerDef"
},
"integerThree": {
"$ref": "#/definitions/integerDef"
}
},
"required": ["integerOne", "integerTwo", "integerThree"],
definitions: {
"integerDef": {
"type": "integer",
"minimum": 200,
"maximum": 500,
"exclusiveMinimum": true,
"exclusiveMaximum": true,
"multipleOf": 20
}
}
}
*/
在本例中,定义的 schema 参数放置在称为 definitions
的特殊对象中。参数的引用放置在每个相关对象中,如上面 IntegerOne
、integerTwo
和 integerThree
属性的示例所示。
用于完成上述操作的代码如下所示。
//Example_2_3
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);
同样,使用的是 JsonSchemaObject (string object_name,JSON_POINTER_TYPE ref_type,string json_schema_reference_uri,string description,bool is_required = false)
构造函数,但这次它传递了 JSON_POINTER_TYPE.DEFINITIONS
作为参数。此外,不是像内部引用那样传递对象 ID,而是使用对象名称,即 integerDef
。
另一个定义 URI 的示例是 "$ref": "#/definitions/jsonObject/integerDef"
。此 URI 表示 integerDef
位于一个名为 jsonObject
的 JSON 对象类型内部,而 jsonObject
又位于 definitions
对象内部。
如果在作用域内或包含 $ref
属性的对象父作用域内的所有定义对象中都找不到引用的对象,则在反序列化过程中会抛出异常。
引用类型及其关联的 URI 可以从 JsonSchemaObject
类的 SchemaReferenceType
和 SchemaReferenceUri
属性中读取。
外部引用
在到目前为止讨论的内部和定义引用机制中,被引用的 schema 位于被引用对象所在的同一对象内部。JSON 允许引用位于对象外部的 schema,因此称为外部引用。
//Example_2_4
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "http://x.y.z/rootschema.json#",
"title": "refExamples",
"description": "External Schema",
"type": "object",
"properties": {
"integerExternal": {
"id": "integer_id_024",
"type": "integer",
"minimum": 200,
"maximum": 500,
"exclusiveMinimum": true,
"exclusiveMaximum": true,
"multipleOf": 20
}
}
*/
典型的外部 schema 的结构类似于上面的 JSON 文本。请注意,顶层对象的标识是一个链接,即 "http://x.y.z/rootschema.json#"
。 从外部引用 integerExternal
是使用此链接和 integerExternal
的标识完成的,如下所示。
//Example_2_4
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-024",
"title": "refExamples",
"description": "REF Demo Schema",
"type": "object",
"properties": {
"integerOne": {
"$ref": "http://x.y.z/rootschema.json#integer_id_024"
},
"integerTwo": {
"$ref": "http://x.y.z/rootschema.json#integer_id_024"
},
"integerThree": {
"$ref": "http://x.y.z/rootschema.json#integer_id_024"
}
},
"required": ["integerOne", "integerTwo", "integerThree"]
}
*/
用于完成上述操作的代码包含在 Example_2_4
例程中。请参阅下面的摘录。
/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);
...
如果被引用的对象位于外部 schema 中,则应用程序有责任在调用反序列化例程之前检索此外部 schema。否则,在反序列化过程中会抛出异常,因为找不到 schema。 在调用 Deserialize
例程之前,使用任何一个 AddSchemaRef
例程来添加外部 schema(或 schemata)。 请参阅 Example_2_4
例程了解如何实现这一点。 AddSchemaRef
例程来添加外部 schema(或 schemata),然后再调用 Deserialize
例程。请参阅 Example_2_4
例程了解如何实现。
引用类型及其关联的 URI 可以从 JsonSchemaObject
类的 SchemaReferenceType
和 SchemaReferenceUri
属性中读取。
JSON 数组类型属性
在本作品的第一部分中,引入了 JSON 数组类型的概念。本节回顾了之前讨论的一些数组参数,并介绍了新的参数。
唯一、最小和最大项
//Example_2_5
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-025",
"title": "arrayExamples",
"description": "Array Demo Schema",
"type": "object",
"properties": {
"arrayObject": {
"type": "array",
"items":
{
"type": "integer"
},
"minItems": 1,
"maxItems": 4,
"uniqueItems": true
}
},
"required": ["arrayObject"]
}
*/
上面显示的 schema 文本现在应该读者很熟悉了。它是一个描述 JSON 数组类型的 schema,该数组类型由 JSON 整数类型的项组成。此数组 schema 的实例应包含不多于 4 个整数项,不少于 1 个。数组中的所有项都必须是唯一的。
如下面的代码摘录所示,使用构造函数 JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,bool additional_items,string description,bool is_required = false)
创建了数组的 schema。
//Example_2_5
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);
对于 Example_2_5
,使用下面的代码创建了上述 schema 的实例。
//Example_2_4
_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)));
}
/* Line 1:
_instance_object_list.Add(new JsonInstanceObject(_json_schema_integer,(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);
要违反数组设置的最大和最小项数,请将 _array_size
变量的值更改为小于 1 或大于 4 的值。异常是怎么说的?
要违反 JSON 数组的 uniqueItems 约束,请注释掉第 1 行并运行 Example_2_5
。读取抛出的异常。
JSON 数组参数 uniqueItems
、minItems
、maxItems
可以从 JsonSchemaObject
类的 UniqueItems
、MinimumItems
、MaximumItems
属性中读取。
下面是数组 schema 的序列化实例。
Example_2_5
/*
{
"arrayObject": [1, 2, 3, 4]
}
*/
数组元组
与直觉相反,JSON 允许数组类型由非相同 JSON 类型项组成。例如,一个数组可以在同一个有序列表中包含字符串、整数和数字。这种数组称为元组。下面是描述元组的 schema 示例。
//Example_2_6
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-026",
"title": "arrayExamples",
"description": "Array Tuple Demo Schema",
"type": "object",
"properties": {
"arrayObject": {
"type": "array",
"items": [
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
},
{
"type": "string",
"enum": ["NW", "NE", "SW", "SE"]
}
],
"uniqueItems": true
}
},
"required": ["arrayObject"]
}
*/
数组项由 1 个整数和 3 个字符串组成。此示例是一个描述地址的元组。
创建此元组所用的 C# 代码如下所示。
//Example_2_6
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);
使用下面的代码创建描述元组的 schema 的实例。
//Example_2_6
_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);
元组 schema 的序列化输出如下所示;
//Example_2_6
/*
{
"arrayObject": [1600, "Pennsylvania", "Avenue", "NW"]
}
*/
附加项
请记住,用于创建元组数组的构造函数是 JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,bool additional_items,string description,bool is_required = false)
。要理解 additional_items
参数在构造函数中所起的作用,请考虑地址元组未提供 State 字段的配置。但是,将 additional_items
参数设置为 true
允许在实例化 schema 时向元组添加额外的字段,而无需显式定义这些额外字段。在 Example_2_7
中,State 字段已添加,如下所示。
//Example_2_7
...
_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"));
...
序列化输出表示如下。
//Example_2_7
/*
{
"arrayObject": [1600, "Pennsylvania", "Avenue", "NW", "Washington"]
}
*/
在 Example_2_7
中将元组 additional_items
参数设置为 false
,以查看是否抛出了异常。
通过将 additional_items
参数的值设置为 true
,可以向元组添加任何 6 种 JSON 类型。但是,有时 additionalItems
属性必须符合特定 schema。例如,可以将可以添加到元组的类型限制为仅 JSON 字符串类型。下面的 schema 正是这样做的。
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-028",
"title": "arrayExamples",
"description": "Array Additional Items Demo Schema",
"type": "object",
"properties": {
"arrayObject": {
"type": "array",
"items": [
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "string",
"enum": ["Street", "Avenue", "Boulevard"]
},
{
"type": "string",
"enum": ["NW", "NE", "SW", "SE"]
},
],
"uniqueItems": true,
"additionalItems":
{
"type": "string"
}
}
},
"required": ["arrayObject"]
}
*/
请注意,数组的 additionalItems
属性现在有一个 schema 作为值,而不是之前分配的布尔值。
要将附加项属性的值设置为 schema,请使用第二个数组构造函数,即 JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,JsonSchemaObject additional_items,string description,bool is_required = false)
, 以实例化 JSON 数组对象,如 Example_2_8
中所做的那样。请参阅下面的代码。
//Example_2_8
...
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);
...
请注意,additionalItems
属性的默认值为 true
。当 additionalItems
属性未在 JSON 数组 schema 中出现时,假定其设置为 true
。
如果 additionalItems
属性为布尔类型,则可以从 JsonSchemaObject
类的 AdditionalItemsBool
属性中读取其值。如果它是 schema 类型,则应从 JsonSchemaObject
类的 AdditionalItemsObject
属性中读取。
JSON 对象类型属性
本节介绍 JSON 对象类型的所有属性。
附加属性
additionalProperties
属性对于 JSON 对象类型 schema 来说,就像 additionalItems
对于 JSON 数组类型 schema 一样。 并且,就像 JSON 数组的 addItems 一样,additionalProperties
属性可以接受布尔值或 schema 值。additionalProperties
的默认值为 true
,并且在 JSON 对象 schema 中未将其作为属性出现时,假定其设置为 true
。在下面显示的 schema 中,additionalProperties
假定为 true。
//Example_2_9
{
"$schema": "https://json-schema.fullstack.org.cn/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"]
}
请注意,上面显示的 schema 中,jsonObject
属性与数组元组 arrayObject
(在前一节中讨论过,请参见 Example_2_8
例程)的属性相似。
JsonSchemaObject (string object_name,int? minimum_properties,int? maximum_properties,JsonSchemaObject additional_properties,string description,bool is_required = false)
构造函数用于实例化 jsonObject
,如下面的代码所示。
//Example_2_9
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);
由于 additionalProperties
参数默认为 true
,因此可以将 state
属性添加到上面 schema 的实例中,如下面的序列化实例文本所示。
//Example_2_9
/*
{
"jsonObject": {
"houseNumber": 1600,
"streetName": "Pennsylvania",
"streetType": "Avenue",
"direction": "NW",
"state": "Washington"
}
}
*/
将 additionalProperties
参数设置为 false,然后运行 Example_2_9
例程,查看抛出的异常。
如前所述,additionalProperties
属性的值也可以是一个 schema,用于限制在实例化 schema 时可以添加的 JSON 类型。要做到这一点,请使用 JsonSchemaObject (string object_name,int? minimum_properties,int? maximum_properties,JsonSchemaObject additional_properties,string description,bool is_required = false)
构造函数。 如果使用此构造函数,则 schema 的序列化输出应类似于下面的文本。
//Example_2_10
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0210",
"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"],
"additionalProperties":
{
"type": "string"
}
}
},
"required": ["jsonObject"]
}
*/
如果需要实例化的 JSON 对象是顶层对象,则使用以下任一构造函数代替
JsonSchemaObject (JSON_VERSION version,string title,string description,string object_id,bool additional_properties = true)
或JsonSchemaObject (JSON_VERSION version,string title,string description,string object_id,JsonSchemaObject additional_properties)
.
additionalProperties
参数可以根据其值类型从 JsonSchemaObject
类的 AdditionalPropertiesBool
或 AdditionalPropertiesObject
属性中读取。
模式属性
除了通过将 additionalProperties
值设置为特定的 schema 定义来限制它之外,JSON schema 规范还提出了另一种称为模式属性 (Pattern Properties) 的机制,它提供了另一层控制附加属性可以采用的结构。 模式属性最好通过示例来解释,使用下面的代码片段。
/Example_2_13
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0213",
"title": "objectExamples",
"description": "OBJECT Pattern Properties Demo",
"type": "object",
"patternProperties": {
"^S_[a-zA-Z0-9]*$": {
"type": "string",
"minLength": 1,
"maxLength": 255
},
"^N_[a-zA-Z0-9]*$": {
"type": "number",
"minimum": 1,
"maximum": 80,
"multipleOf": 5,
}
}
}
*/
上面 patternProperties
属性的子属性强制执行一个规则,该规则规定任何以 "S_" 开头的附加属性名称必须是 JSON 类型字符串,并且字符长度不得小于 1 且不得大于 255。相应地,任何名称以 "N_" 开头的附加属性必须是 JSON 类型数字,值必须在 1 到 80 之间,并且必须是 5 的倍数。
上述 schema 是使用下面的代码片段生成的(请参见 Example_2_13
例程)。
//Example_2_13
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);
使用 AddPatternProperty (JsonSchemaObject json_schema_object, string property_pattern)
例程将模式属性 (patternProperties) 添加到 JSON 对象类型,如上所示。
使用下面的代码片段创建 schema 的实例
//Example_2_13
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);
_json_instance_number
和 _json_instance_string
对象的 patternProperties 定义所施加的限制是一致的。将传递给 _json_instance_number
的值更改为 100。有什么理由会抛出异常吗?
下面是 schema 实例的序列化文本表示。
//Example_2_13
/*
{
"N_25": 60,
"S_24": "Donkey Hotey"
}
*/
有关模式属性 (Pattern Properties) 的更多示例,请参阅 Example_2_14
、Example_2_15 和 Example_2_16
例程。
必需、最小和最大属性
required
属性在第一部分中已讨论过。正式来说,它是一个属性名称的有序列表,在实例化对象类型的 schema 时必须存在。在此类中,如果在实例化 JSON 对象 schema 时,缺少标记为必需的属性,则会抛出异常。
minProperties
和 maxProperties
控制 JSON 对象类型允许拥有的属性数量的下限和上限。在下面显示的 schema 中,jsonObject
属性允许拥有 1 到 6 个属性,不多不少。
//Example_2_11
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0211",
"title": "objectExamples",
"description": "OBJECT Max and Min Properties Demo",
"type": "object",
"properties": {
"jsonObject": {
"type": "object",
"properties": {
"objectString": {
"type": "string"
}
},
"required": ["objectString"],
"minProperties": 1,
"maxProperties": 6
}
},
"required": ["jsonObject"]
}
*/
请参阅下面的 Example_2_11
代码片段,了解如何设置 minProperties
和 maxProperties
值。
//Eaxmple_2_11
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);
下面的 jsonObject
schema 实例化代码片段除了在 schema 中显式定义的 objectString
属性外,还添加了四个额外项。 这被允许,因为 additionalProperties
参数设置为 true。
//Example_2_11
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);
如下面的序列化实例文本所示,这使得 schema 实例的属性总数为 5。将 Example_2_13
例程中的 _extra_properties_count
值更改为大于或等于 6 的数字,并查看当属性数量超过 maxProperties
值时是否抛出异常。
//Example_2_11
/*
{
"jsonObject": {
"objectString": "main-string_value",
"extraString-1": "extra-string_value1",
"extraString-2": "extra-string_value2",
"extraString-3": "extra-string_value3",
"extraString-4": "extra-string_value4"
}
}
*/
minProperties
和 maxProperties
值可以从 JsonSchemaObject
类的 MinimumProperties
和 MaximumProperties
属性中读取。
依赖项
//Example_2_12
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0212",
"title": "objectExamples",
"description": "OBJECT Dependencies Properties
"type": "object",
"properties": {
"billingObject": {
"type": "object",
"properties": {
"holderName": {
"type": "string"
},
"creditCard": {
"type": "number"
},
"billingAddress": {
"type": "string"
}
}
}
},
"required": ["billingObject"]
}
*/
从上面的 schema 可以看出,billingObject
有 3 个属性,即 holderName
、creditCard
和 billingAddress
。虽然 billingObject
是必需的,但它的 3 个属性不是。
设想一个场景,其中一个 JSON 对象属性不能独立于另一个存在。例如,如果存在 creditCard
属性,则 billingAddress
属性也必须存在,反之亦然。换句话说,creditCard
和 billingAddress
属性相互依赖。 JSON schema validation 规范可以使用 dependencies
关键字强制执行此规则。
JSON schema validation 规范定义了两种 dependencies
类型:Schema 和 Property。这两种 dependencies
类型在功能上是相似的。下面的代码显示了如何使用这些类来实现 dependencies
。
//Example_2_12
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}));
/* Line 1
_json_schema_billing_object.AddDependencies (JSON_DEPENDENCY_TYPE.SCHEMA,
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);
依赖对象是使用 JsonSchemaObject (JsonSchemaObject dependent_object,List<JsonSchemaObject> dependency_object_list)
构造函数创建的。在上面的代码行中,此构造函数的第一参数是被依赖对象,第二个参数是该被依赖对象所依赖的对象列表。上面的 _json_schema_credit_card_dependency
schema 对象是一个 dependencies
对象,它表示 creditCard
属性依赖于 holderName
和 billingAddress
属性。_json_schema_billing_address_dependency
schema 对象表示 billingAddress
属性依赖于 creditCard
属性。
使用 AddDependencies (JSON_DEPENDENCY_TYPE dependency_type,List<JsonSchemaObject> json_dependency_object_list)
例程将依赖项添加到 JSON 对象类型。传递给此例程的第一个参数是所需的依赖项类型。第二个参数是要添加到 JSON 对象中的依赖项列表。
序列化后,输出字符串应类似于下面的内容;
//Example_2_12
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0212",
"title": "objectExamples",
"description": "OBJECT Dependencies Properties
"type": "object",
"properties": {
"billingObject": {
"type": "object",
"properties": {
"holderName": {
"type": "string"
},
"creditCard": {
"type": "number"
},
"billingAddress": {
"type": "string"
}
},
"dependencies": {
creditCard: [holderName, billingAddress],
billingAddress: [creditCard]
}
}
},
"required": ["billingObject"]
}
*/
上面显示的 JSON schema 字符串是属性 dependencies
类型的一个示例。
要获得下面显示的 Schema dependencies
类型的表示,请注释掉 Example_2_12
代码中的“第 1 行”。
//Example_2_12
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0212",
"title": "objectExamples",
"description": "OBJECT Dependencies Properties Demo",
"type": "object",
"properties": {
"billingObject": {
"type": "object",
"properties": {
"holderName": {
"type": "string"
},
"creditCard": {
"type": "number"
},
"billingAddress": {
"type": "string"
}
},
"dependencies": {
creditCard: {
"properties": {
"holderName": {
"type": "string"
},
"billingAddress": {
"type": "string"
}
},
"required": ["holderName", "billingAddress"]
},
billingAddress: {
"properties": {
"creditCard": {
"type": "number"
}
},
"required": ["creditCard"]
}
}
}
},
"required": ["billingObject"]
}
*/
下面是上述 schema 的实例。
// Example_2_12
{
"billingObject": {
"holderName": "Donkey Hotey",
"creditCard": 234555554444,
"billingAddress": "No. 2 North West Street"
}
}
作为测试,在创建实例时排除 creditCard
属性,方法是注释掉 Example_2_12
例程中的“第 2 行”,以查看抛出了什么异常。
JSON Schema 约束属性
//Example_2_17
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0217",
"title": "objectExamples",
"description": "OBJECT Constraint Demo",
"type": "object",
"properties": {
"anonObject": {
"oneOf": [
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "boolean"
}
]
}
},
"required": ["anonObject"]
}
*/
上述 schema 的解释表明,在实例化 schema 时,anonObject
属性可以是字符串、整数或布尔类型。oneOf
关键字为 JSON 属性提供了灵活性,但也有一些限制。灵活性在于属性可以是 JSON 类型 A 或类型 B,并且仍然有效。约束在于这种灵活性受 oneOf
关键字中定义的类型限制。
除了 oneOf
约束之外,JSON 规范还定义了三种约束类型,即 not
、allOf
和 anyOf
。请参阅下面这些约束的列表项解释;
oneOf
: schema 的实例必须匹配关键字定义的 schema(schemata)中的一个且仅一个。not
: schema 的实例可以采用除关键字定义的类型之外的任何类型。anyOf
: schema 的实例可以匹配关键字定义的任何一个 schema(schemata)。allOf
: schema 的实例必须匹配此关键字定义的所有 schema(schemata)。
用于创建和序列化上面 anonObject
schema 文本的代码片段如下所示。
//Example_2_17
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);
请注意,anonObject
对象使用此构造函数实例化:JsonSchemaObject(string object_name,JSON_CONSTRAINT_TYPE object_constraint,string description,bool is_required = false)
。 注意传递给构造函数的第二个参数是约束类型,即 JSON_CONSTRAINT_TYPE.ONEOF
。 约束的 schema(schemata)是使用 AddObject (
JsonSchemaObject json_schema_object)
例程添加的,如上所示。
anonObject
属性是使用下面的代码实例化的。
//Example_2_17
JsonInstanceObject _json_instance_anon_object = null;
_json_instance_anon_object = new JsonInstanceObject (new JsonSchemaObject("anonObject",
JSON_TYPE.BOOLEAN,string.Empty),true);
/* Line 1
_json_instance_anon_object = new JsonInstanceObject (new JsonSchemaObject ("anonObject",
JSON_TYPE.NUMBER, string.Empty), 60);
//*/
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);
在上面的代码中,anonObject
的值被设置为布尔类型,这当然是 oneOf
属性中设置的三种有效 schema 之一。注释掉 Example_2_17
代码中的第 1 行标记部分并再次运行该示例。有什么理由会抛出那个异常吗?
下面是 anonObject
schema 实例的序列化输出。
//Example_2_17
/*
{
"anonObject": true
}
*/
有关 JSON schema 约束的更复杂示例,请参阅 Example_2_18
例程。
关注点
与第一部分一样!;)
历史
2015/01/04: 第一个版本。
2015/02/04: 修改了 JSON String 类型 schema 构造函数。
2016/03/05: 修改了此 JSON 库的 DoEscape 函数,使其符合www.json.org 的转义规范。
2016/03/11: 修正了 JSON 反转义函数。用 Net Core 版本替换了附加的代码。