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






4.94/5 (20投票s)
一套用于创建和操作 JSON Schema 和实例的 C# 类。
引言
需要一种方式来定义一个表单,并将此表单从数据库服务器下载到移动、Web 或桌面平台。填写好的表单随后提交给数据库服务器。关于如何定义这些表单,我考虑了两种选择:XML 和 JavaScript 对象表示法 (JSON)。起初强烈倾向于 XML,但 PostgreSQL 的 jsonb 数据类型允许高效存储和查询 JSON 数据,这使得 JSON 占据了优势。
解决了这个问题后,便开始寻找一个完美的 C# JSON 库。最完整的是 NewtonSoft JSON 库。然而,最终决定在内部开发一个库(或一组类)。想法是,如果填写好的表单字段与表单中定义的字段不一致,则创建表单数据实例应失败。使用 NewtonSoft,必须首先创建数据实例,然后检查它是否与 schema 中定义的对应。
上述以及这是一个了解 JSON 的机会的事实,是这项工作的动力。
在这项工作的第一部分中,介绍了有效创建 JSON schema 和实例所需的步骤。在第二部分中,讨论了更高级的 JSON 功能。
背景
根据上一节中阐述的目标,JSON 指定了定义表单的机制。这些机制统称为 JSON Schema 对象。一旦定义,schema 可用于指导 JSON 数据实例对象的创建。明确地说,JSON Schema 用于定义表单,而填写好的表单称为该表单的一个实例。
JSON schema 核心 规范 定义了 4 种原始数据类型(即 String、Integer、Number、Boolean 和 Null)和 2 种复合数据类型(即 Array 和 Object)。JSON Array 对象类型是零个或多个所有或任何 6 种 JSON 类型的有序列表。而 JSON Object 数据类型是零个或多个所有或任何 6 种类型的无序列表。
根据 JSON 实例 规范,将编程语言对象转换为有效 JSON schema 或实例字符串的机制称为生成器。解析器是将有效 JSON 字符串转换为编程语言对象的机制。对于这项工作,生成器将称为序列化器,解析器称为反序列化器。
目录
使用代码
在接下来的小节中,将尝试演示如何创建映射到所有 JSON 类型的 C# 对象。还介绍了如何将 C# 对象序列化为 JSON schema 和实例字符串,以及如何将 JSON 字符串反序列化回 C# 对象。本节将总结如何按名称查找 JSON 对象,以及字符在序列化和反序列化过程中如何转义和反转义。
这些 JSON C# 类集中的许多机制将通过示例进行演示。所呈现的示例假设要创建一个并填写求职申请表。
顶级 JSON Schema 对象
理想情况下,JSON schema 的所有相关组件都应包含在顶级 schema 对象中。顶级对象描述表单,为其提供标题、标识,但最重要的是告知表单符合哪个版本的 JSON schema 标准。请注意,这项工作基于 JSON schema 规范的第 4 版。
//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);
上面显示了创建顶级 JSON schema 对象的代码。可以看出,schema 对象构造函数 JsonSchemaObject (JSON_VERSION version,string title,string description,string object_id,bool additional_properties = true)
传递了表单的版本、标题、描述和标识。目前,请忽略 additional_properties
参数,因为本工作的第二部分会对其进行处理。
JsonSchemaEngine 对象被实例化并用于序列化顶级对象。_schema_string
的内容如下所示。
//output of Example_1_1
/*
{
"$schema": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"type": "object",
"id": "FormID-001",
"title": "PersonInfo",
"description": "Prospective Employee Detail Form"
}
*/
上面 JSON schema 中的 $schema
属性表示该表单符合 schema 规范的第 4 版草案。type
属性表示顶级对象是 JSON Object 类型,因此可以包含其他 JSON 类型。其余属性不言自明。
顶级 C# schema 对象的 version
、type
、id
、title
和 description
可以分别从以下 JsonSchemaObject
的 Version
、ObjectType
、ObjectID
、Title
和 Description
属性中读取。
上述代码可以在附加解决方案的 Example_1_1
函数中找到。
JSON Schema 字符串类型
假设表单的前两个字段是申请人的名字和姓氏。这些字段都是 JSON 字符串类型,每个字段的长度必须在 2 到 100 个字符之间。这两个字段都是必填的。创建这些字段的代码如下所示:
// 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);
可以使用以下 JsonSchemaObject
构造函数创建 schema 的 JSON 字符串类型:JsonSchemaObject (string object_name,int? minimum_length, int? maximum_length,string pattern,string description,bool is_required = false)
。
当这些字段的 schema 对象创建后,它们会使用 JsonSchemaObject AddObject (JsonSchemaObject json_schema_object)
实例例程添加到顶级对象,即 _json_schema_person_details
,如上所示。
顶级对象的序列化字符串如下所示:
//Example_1_2
/*
{
"$schema": "https://json-schema.fullstack.org.cn/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"]
}
*/
当其他 JSON 类型被添加到 JSON Object 类型时,这些添加的类型称为对象的属性。这就是为什么名字和姓氏字段包含在标记为属性的花括号内的原因。请注意,在上面的字符串末尾,firstName
和 lastName
对象被标记为 required
。这是因为这两个对象的 is_required
参数都设置为 true
。这些字段不需要符合任何字符串模式,因此字符串模式属性设置为 string.Empty
。本作品后面将给出一个示例,展示如何设置模式属性。
以下 JsonSchemaObject 属性仅与 JSON 字符串类型的 C# 对象相关联:MaximumLength
、MinimumLength
和 Pattern
。JsonSchemaObject
对象的名称可以从 ObjectName
属性中读取。
以下代码片段展示了如何创建表单的当前实例。
//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);
创建 JsonSchemaObject 实例涉及调用 JsonInstanceObject (JsonSchemaObject json_schema_object, string json_instance_value)
构造函数,并像上面所示一样,将定义的 schema 和实例值都传递给它。
如果作为名字或姓氏传递的字符串值违反了最小或最大字符长度,则会立即抛出异常,并且不会创建实例。
这些名字和姓氏实例对象一经创建,就会作为列表传递给 _json_schema_person_details
schema 对象的实例对象,使用以下构造函数 JsonInstanceObject (JsonSchemaObject json_schema_object, List<JsonInstanceObject> json_instance_object_list)
。
请注意,_json_schema_person_details
schema 对象也传递给了_json_instance_person_details
以及实例对象列表。这确保了生成的表单实例符合表单 schema 中设置的参数。例如,如果实例列表中不包含 firstName
或 lastName
对象,则会抛出异常。回想一下,这两个属性都是必需的。除其他检查外,还会进行一项检查,以确保 firstName
或 lastName
对象都是 schema 中定义的 JSON 字符串类型。JSON 实例对象的序列化过程与 JSON schema 的序列化过程类似。序列化后的实例字符串输出如下所示:
//Example_1_2
/*
{
"firstName": "Donkey",
"lastName": "Hotey"
}
*/
如果 JSON 实例是字符串类型,则通过读取 JsonInstanceObject
类的 ObjectStringValue
属性获取实例值。JSON 实例 C# 对象的名称存储在 JsonInstanceObject
类的 ObjectName 属性中。
完整代码请参见 Example_1_2
例程。
JSON Schema Enum 约束和默认值
JSON 定义了一个数组 enum
关键字,有助于限制 JSON 实例对象可以取值的范围。例如,表单可以将申请人的性别限制在五种选择之一。实现此约束的步骤如下所示。
// 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);
可以看出,创建了一个 JSON 字符串类型的性别对象,并使用 JsonSchemaObject AddEnumList (object object_enum,int default_index = -1)
例程将性别属性的可能值列表附加到该对象上。
序列化后的 _json_schema_person_detail
的输出如下所示。
//Example_1_3
/*
{
...
"properties": {
...
"gender": {
"description": "Gender of Applicant",
"type": "string",
"enum": ["male", "female", "transgender", "intersex", "other"],
"default": "female"
}
},
"required": ["firstName", "lastName", "gender"]
}
*/
请注意,性别对象的 default
值设置为 female。这是因为整数值 1 被作为第二个参数传递给 AddEnumList
例程。1 是 _gender_options
列表中字符串 "female"
的索引。如果不需要 default
值,则该参数应省略或设置为 -1。也可以使用 SetDefaultValue (object default_value)
例程独立于 AddEnumList
例程设置默认值。有关如何使用此例程为所有 6 种 JSON 类型设置默认值的用例,请参见 Example_1_14
。
请注意,传递给 AddEnumList
例程列表中的所有对象都必须是唯一的,否则将抛出异常。
//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);
上面的代码展示了如何创建 _json_schema_gender
对象的实例。创建后,此性别实例被添加到 _json_instance_person_details
对象中,然后序列化以产生下面的字符串输出。
Example_1_3
/*
{
"firstName": "Donkey",
"lasttName": "Hotey",
"gender": "other"
}
*/
如果 _json_instance_gender
传递的字符串值不是 enum 数组中的值,则会抛出异常。另一方面,如果传入空字符串或 null 字符串作为其值,则其值将变为默认值,即 "female"。上述代码包含在 Example_1_3
例程中。
所有 6 种 JSON 类型都可以分配 enum
数组和 default
值。对于 JSON Number、Integer 和 Boolean 类型,分别将 C# double?
、int?
和 bool?
列表传递给 AddEnumList
例程。
另一方面,对于 JSON Object 和 Array 类型,传递一个 JsonInstanceObject
列表。演示如何完成上述操作的代码在附加解决方案文件中的 Example_1_8
和 Example_1_9
中给出。
Enum 列表包含在 JsonSchemaObject
类的 EnumInstanceObject
属性的对象列表中。default
值可以从同一类的 DefaultInstanceObject
属性中读取。
JSON Schema Integer 和 Number 类型
在这项工作中,JSON Integer 类型映射到 C# int?,Number 类型映射到 double?。除此之外,JSON Integer 和 Number 类型具有相同的对象属性集,并且以类似的方式进行操作。考虑到这一点,假设表单中需要一个 JSON Integer 类型的年龄字段。还假设此字段的值必须在 18 到 55 之间。构造函数 JsonSchemaObject (string object_name,JSON_TYPE json_type, double? minimum_value,double? maximum_value, double? multiple_of_value, string description,bool is_required = false)
用于创建此字段,如下所示:
//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);
此构造函数只接受 JSON_TYPE.NUMBER
和 JSON_TYPE.INTEGER
作为其第二个参数,否则将抛出异常。multiple_of_value
参数设置为 null,因为它不是必需的。但如果表单希望将 _json_schema_age
对象的实例值限制为 5 的倍数,则该参数应设置为 5。在这种情况下,分配的值不是此参数值的倍数,则会抛出异常。
现在,顶级对象的序列化字符串应该有一个名为 age
的属性,如下所示
//Example_1_4
/*
{
...
"properties": {
...
"age": {
"description": "The Age of the Applicant ",
"type": "integer",
"minimum": 18,
"maximum": 55
}
},
"required": ["firstName", "lastName", "gender", "age"]
}
*/
JSON Integer 或 Number 类型特有的 JsonSchemaObject
属性如下:MaximumValue
、MinimumValue
和 MultipleOfValue
。还有两个与 JSON Integer 或 Number 类型相关的属性,即 ExclusiveMinimum
和 ExclusiveMaximum
。将 ExclusiveMinimum
属性设置为 true
意味着传递给 _json_schema_age
对象实例的值必须排除 18,即有效值从 19 开始。类似地,如果 ExclusiveMaximum
属性设置为 true
,则 _json_schema_age
实例的最高有效值为 54。ExclusiveMinimum
和 ExclusiveMaximum
属性的默认值为 false
。
紧随其后的代码片段展示了如何创建年龄 schema 对象的实例。
//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);
如果传递给 JsonInstanceObject (JsonSchemaObject json_schema_object, int? json_instance_value)
构造函数的值小于 18 或大于 55,则会抛出异常。如果 _json_schema_age
对象是 Number 类型,则会调用 JsonInstanceObject (JsonSchemaObject json_schema_object,double? json_instance_value)
构造函数。
如果 JsonInstanceObject
类型是 Number,则其关联值可以从实例类 ObjectDoubleValue
属性中获取。如果是 Integer 类型,则其值可以从类的 ObjectIntegerValue
属性中读取。
序列化表单实例应产生以下输出:
//Example_1_4
/*
{
"firstName": "Donkey",
"lastName": "Hotey",
"gender": "other",
"age": 28
}
*/
完整的代码清单请参见附加解决方案中的 Example_1_4
例程。
为了描述 JSON 布尔类型如何在本工作中实现,假设表单中有一个强制字段,申请人必须说明是否需要进一步培训。此字段的有效值应为 true 或 false。以下代码展示了如何实现上述目标。
//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);
在本工作中,JSON 布尔类型映射到 C# bool?
。
序列化表单现在应该有一个名为 "training?"
的必填属性,如下所示。
// Example_1_5
/*
{
...
"properties": {
...
"training?": {
"description": "Does the applicant require training?",
"type": "boolean",
"enum": [true, false]
},
"required": ["firstName", "lastName", "gender", "age", "training?"]
}
*/
请注意,此属性未设置 default
值。你能猜到原因吗?
// 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);
使用以下构造函数 JsonInstanceObject (JsonSchemaObject json_schema_object, bool? json_instance_value)
创建 _json_schema_requires_training
对象的实例(见上面的代码片段)。与 JSON 布尔类型实例相关联的值可以从 JsonInstanceObject
的 ObjectBooleanValue
属性中检索。
现在,表单的序列化实例应包含 training 属性,如下所示。
//Example_1_5
/*
{
"firstName": "Donkey",
"lastName": "Hotey",
"gender": "other",
"age": 28,
"training?": false
}
*/
完整的代码请参见 Example_1_5
例程。
考虑表单中申请人的地址字段,该字段由四个子字段组成,即门牌号、街道、城市和省份。此字段可以充分表示为具有四个属性的 JSON schema 对象类型,即 1 个 JSON Integer 类型和 3 个 JSON String 类型。创建地址对象的代码行如下所示:
//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);
请注意,address
对象的四个属性使用简写构造函数进行实例化,即 JsonSchemaObject (string object_name,JSON_TYPE json_type,string description,bool is_required = false)
。此构造函数允许创建具有所有其他相关 schema 参数设置为默认值的 JSON schema 对象。地址对象本身也使用此简写构造函数。创建 JSON Object 类型的完整构造函数以及相关的 JsonSchemaObject
属性将在第 2 部分中讨论。
顶级对象的序列化输出应具有一个 address
属性,如下所示:
//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"]
}
*/
读者会注意到,这是一个 JSON 对象类型(即 address
对象)作为另一个 JSON 对象类型(即顶级对象)的属性的示例。
使用 JsonInstanceObject (JsonSchemaObject json_schema_object, List<JsonInstanceObject> json_instance_object_list)
构造函数,_json_schema_address
对象的实例创建如下:
//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);
组成 JSON Object 类型实例的对象包含在 JsonInstanceObject
类的 ObjectList
属性中。
表单实例的序列化输出现在应如下所示:
//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"
}
}
*/
假设表单要求申请人填写联系电话号码。每个号码都必须有一个相关的电话号码类型指定,例如家庭、工作等。此外,申请人的电话号码数字必须以 0 开头,并且必须是 11 位数字。申请人还可以至少填写一个号码,最多三个。
根据上述要求,联系电话号码是一个 2 元组对象,即电话号码类型和电话号码数字。这可以作为具有两个 JSON 字符串类型属性的 JSON Object 类型来容纳。
允许申请人输入 1 到 3 个电话号码对象可以使用 JSON 数组类型实现。
下面的代码展示了如何实现这一点。
//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);
请注意,_json_schema_number 对象的 pattern
参数设置为正则表达式字符串 "0\\d{10}
"。这与电话号码数字的要求一致。
JSON Array 类型对象使用以下构造函数创建:JsonSchemaObject (string object_name,int? minimum_items,int? maximum_items,bool is_unique_items,bool additional_items,string description,bool is_required = false)
。
根据要求,构造函数的 minimum_items
和 maximum_items
参数分别设置为 1 和 3。如果申请人在表单实例化期间输入少于 1 个或多于 3 个号码,则会立即抛出异常。is_unique_items
参数设置为 true
,以确保所有输入的号码对象都是唯一的。如果两个号码实例具有相同的电话号码类型和电话号码数字,则会抛出异常。additional_items
参数将在第 2 部分中介绍。与 JSON 数组类型的 JsonSchemaObject
相关的属性也将在第 2 部分中讨论。
phoneNumber
对象的序列化输出如下所示:
//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"]
}
*/
上面显示的输出是 JSON Object 类型(即 _json_schema_number_object
)包含在 JSON Array 类型(即 _json_schema_number_array
)中,并且 JSON Array 类型(即 _json_schema_number_array
)包含在 JSON Object 类型(即 _json_schema_person_details
)中的一个示例。这演示了三层 JSON 对象的嵌套。
_json_schema_number_array
的一个实例是使用 JsonInstanceObject (JsonSchemaObject json_schema_object, List<JsonInstanceObject> json_instance_object_list)
构造函数创建的,如下所示。
//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);
构成 JSON 数组类型实例的对象可以在 JsonInstanceObject
类的 ObjectList
属性中找到。
表单实例的序列化输出如下所示:
//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"
}
]
}
*/
上述所有相关代码都可以在 Example_1_7
例程中找到。
JSON Schema Null 类型
为了说明 JSON Null 类型如何在本工作中实现,允许在表单中包含一个名为 spouse
的字段。还考虑到该字段在当前版本的表单中不使用,但将来可能需要。确保该字段存在于表单中的众多方法之一是将其指定为 JSON Null 类型。
//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);
序列化上述代码将把 spouse
属性作为顶级 JSON 对象的一个属性,如下所示:
//Example_1_10
/*
{
...
"properties": {
...
"spouse": {
"description": "To be used in the future",
"type": "null"
}
},
"required": ["firstName", "lastName", "gender", "age", "training?", "address",
"phoneNumber", "spouse"]
}
*/
使用 JsonInstanceObject (JsonSchemaObject json_schema_object)
构造函数创建 _json_schema_spouse
对象的实例,如下所示:
//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);
序列化后,表单实例的最终版本将如下所示:
// 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
}
*/
完整的代码请参见 Example_1_10
例程。
反序列化 JSON Schema 和实例
转换或反序列化有效的 JSON schema 字符串类似于序列化过程。
在实例化 JsonSchemaEngine
后,一个有效的 JSON schema 字符串被传递给 Deserialize (string json_schema)
例程,该例程返回 JsonSchemaObject C# 顶级对象。
请注意,通过调用 JsonSchemaEngine(int schema_size_byte_limit = -1)
构造函数,可以限制可反序列化的 JSON schema 字符串的大小(以字节为单位)。如果传递给 Deserialize
例程的 json_schema
参数的大小大于 JsonSchemaEngine
实例化时为 schema_size_byte_limit
指定的值,则会抛出异常。
//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);
反序列化 JSON schema 的实例需要该实例创建时使用的 schema 存在,并将其传递给 JsonInstanceEngine
类的 JsonInstanceObject Deserialize (JsonSchemaObject json_schema_object,string json_instance)
例程,如上所示。
如上所示,还可以通过将 JsonInstanceEngine (int instance_size_byte_limit = -1)
构造函数中的 instance_size_byte_limit
参数设置为指定的最大大小,来限制可反序列化的 JSON 实例字符串的大小。
请参阅 Example_1_11
例程。
按名称查找 JSON Schema 和实例对象
可以通过调用 JsonSchemaObject
静态例程 FindSchemaObjectByName (string json_schema_name, JsonSchemaObject json_schema_object)
并传入要读取的对象的名称以及容器对象来检索和读取 JSON C# schema 对象的组件对象。在下面的示例中,尝试使用此例程从 _json_schema_person_details
对象中检索 gender
schema 对象。如果返回了 gender
对象,则可以读取其相关属性,如下所示。
//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);
}
控制台输出的表示形式如下所示:
//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
*/
JsonSchemaObject
类的 FindSchemaObjectByName
例程有一个用于 JsonInstanceObject
类的类似例程,即 FindInstanceObjectByName (string object_name, JsonInstanceObject json_instance_object)
。下面展示的 Example_1_12
例程的摘录演示了其用法。
// 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
*/
JSON 引擎字符转义
在序列化过程中,JsonSchemaEngine
将尝试转义文本主体中出现的某些特殊 JSON 字符,例如双引号 ' " '。请参阅下面的场景,了解何时会发生这种情况。这些转义的字符串在 schema 反序列化过程中会被反转义。
//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": "https://json-schema.fullstack.org.cn/draft-04/schema#",
"id": "ExampleID-0113",
"title": "xterEscapeExamples",
"description": "Character escape Demo",
"type": "object",
"properties": {
"Quote \"name\"": {
"description": "Testing Xter Escape",
"type": "string"
}
}
}
*/
JsonInstanceEngine
也是如此,如下所示:
//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"
}
*/
恐怕没什么有趣的事情要报告了 ;)
历史
2015年1月4日:第一版。
2015年4月2日:修改了 JSON 字符串类型 schema 构造函数。
2016年5月3日:修改了此 JSON 库的 DoEscape 函数,使其与 www.json.org 转义规范对齐。
2016年11月3日:修正了 JSON 反转义函数。将附加代码替换为 Net Core 版本。