From f1233df7316578d1fe4be9fc55cd96870157f706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=9F=84=E6=B6=B5?= Date: Wed, 28 Jun 2023 17:44:55 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90ylong=5Fjson=E3=80=91=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20README=20&=20RELEASE=5FNOTE=20&=20user=5Fguide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 王柄涵 --- README.md | 319 ++++---------------------- README_zh.md | 286 +++-------------------- RELEASE_NOTE.md | 5 + docs/user_guide.md | 269 ++++++++++++++++++++++ docs/user_guide_zh.md | 235 +++++++++++++++++++ figure/ylong_json_inner_structure.png | Bin 0 -> 21764 bytes figure/ylong_json_oh_relate.png | Bin 0 -> 11805 bytes 7 files changed, 586 insertions(+), 528 deletions(-) create mode 100644 RELEASE_NOTE.md create mode 100644 docs/user_guide.md create mode 100644 docs/user_guide_zh.md create mode 100644 figure/ylong_json_inner_structure.png create mode 100644 figure/ylong_json_oh_relate.png diff --git a/README.md b/README.md index 874f27e..6026817 100644 --- a/README.md +++ b/README.md @@ -1,273 +1,47 @@ # ylong_json ## Introduction - The `ylong_json` module provides serialization of text or string in JSON syntax format and deserialization of corresponding generated instances. -`ylong_json` contains the following core functionality: +### ylong_json in Openharmony +![structure](./figure/ylong_json_oh_relate.png) +Here is the description of the key fields in the figure above: +- `ylong_json` : System component that provides json serialization and deserialization capabilities +- `serde` : Third-party library for efficient and generic serialization and deserialization of Rust data structures. -### Function 1: Generates a JSON instance -`ylong_json` provides the ability to generate an instance of `JsonValue` from JSON text or string. You need to use a series of instance creation methods for the "JsonValue" to use this feature. +### ylong_json Internal architecture diagram +![structure](./figure/ylong_json_inner_structure.png) +`ylong_json` is mainly divided into two modules, a module with a custom `JsonValue` structure type as the core and a module that ADAPTS to the third-party library `serde`. -(1) You can create a `JsonValue` instance by: -```rust -use std::fs::File; -use std::str::FromStr; -use std::io::Read; -use ylong_json::JsonValue; -fn create_json_value_instance() { - let str: &str = ""; - // You can use `from_str` to try to generate a `JsonValue` instance from - // the &str type. - // If the passed &str does not conform to JSON syntax, the corresponding - // Error will be returned. - let json_value = JsonValue::from_str(str); - - let text: String = String::from(""); - // You can use `from_text` to generate a `JsonValue` instance from - // a series of types that implement AsRef<[u8]>. - // If the passed text content does not conform to JSON syntax, the - // corresponding Error will be returned. - let json_value = JsonValue::from_text(text); - - let path: &str = ""; - // You can use `from_file` to read a file from corresponding path and - // try to generate a `JsonValue` instance. - // If the passed path is not valid or the text content does not conform - // to JSON syntax, the corresponding Error will be returned. - let json_value = JsonValue::from_file(path); - - let mut reader: Box = Box::new(File::open("").unwrap()); - // You can use `from_reader` interface to read text from an instance - // that implements io::Read and try to generate a `JsonValue` instance. - // If the read fails or if the content from the reader does not conform - // to JSON syntax, the corresponding Error will be returned. - let json_value = JsonValue::from_reader(&mut reader); -} +1. `JsonValue` is the internal custom structure type of `ylong_json`, and the serialization and deserialization function of `json` is built with this structure as the core. +- `JsonValue` : The core structure type, which stores the json content information, has 6 internal enum type variants. +- `LinkedList`, `Vec`, `BTreeMap` : Three ways of storing data inside `Array` and `Object`, selected by `features`. +- Serialization ability: Supports outputting a `JsonValue` instance as a compact strings or writing to the output stream. +- Deserialization ability: Supports parsing `json` text or `json` content in the input stream and generating a `JsonValue` instance. + +2. `ylong_json` adapts to the third-party library `serde` +- `Serializer`: The auxiliary structure for serialization. +- `Deserializer`: The auxiliary structure for deserialization. +- Serialization ability: Supports for serializing a type instance that implements the `serde::Serialize` trait into `json` text content or writing the content to the output stream. +- Deserialization ability: If the `json` content has the type that implements `serde::Deserialize` trait, then that part of the `json` content can be deserialized into an instance of that type. + +## Directory ``` -Once the `JsonValue` instance has been successfully created, you can attempt to read and modify the corresponding contents. - -(2) If the type in the JSON text implements the third-party library `serde::Deserialize` trait, you can directly deserialize the text content to an instance of that type. -```rust -use std::fs::File; -use serde::Deserialize; -use ylong_json::deserializer::{from_reader, from_slice, from_st}; -fn deserialize_json_to_instance() { - #[derive(Deserialize, PartialEq, Debug)] - struct Example { - int: u32, - seq: Vec, - tup: (i32, i32, i32), - } - - // You can use `from_str` to try to generate an instance from String. - // If the passed String does not conform to JSON syntax, the corresponding - // Error will be returned. - let str = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; - let example = from_str::(str).unwrap(); - - // You can use `from_slice` to try to generate an instance from &u8. - // If the passed &u8 does not conform to JSON syntax, the corresponding - // Error will be returned. - let slice = str.as_bytes(); - let example = from_slice::(slice).unwrap(); - - // You can use `from_reader` to try to generate an instance from - // locations, files, io streams, and so on that implement io::Write. - // If the passed text content does not conform to JSON syntax, - // the corresponding Error will be returned. - let mut file: File = File::open("./example.txt").unwrap(); - let example = from_reader::(file).unwrap(); -} -``` - -### Function 2: Reads and modifies a key-value pair -After a `JsonValue` instance is successfully generated, you can use a subscript to find the corresponding key-value pair (to obtain a common reference to the corresponding `JsonValue`). - -A subscript of type &str or String can be used to find a key-value pair in Object; -A Subscript of type usize can be used to find a key-value pair in an Array. -```rust -use std::str::FromStr; -use ylong_json::JsonValue; - -// JSON string for the example -const JSON_TEXT: &str = r#" -{ - "key": "value", - "array": [1, 2, 3] -} -"#; - -fn find_key_value_pair() { - // Creates a JsonValue instance from the example string, the syntax is - // correct so the parse must succeed here, so uses unwrap. - let json_value = JsonValue::from_str(JSON_TEXT).unwrap(); - - // Since json is itself a table, you can use the &str type to obtain - // a common reference to the internal value. - let value: &JsonValue = &json_value["key"]; - - // You can use the &str type to obtain a common reference to the "array" member, and - // then use the usize type to obtain a common reference to the corresponding element. - let array_item: &JsonValue = &json_value["array"][0]; - - // If you try to find a key that does not exist in a table, - // `&JsonValue::Null` will be returned. - let no_such_key: &JsonValue = &json_value["no_such_key"]; - - // When searching for the Array type, if the subscript exceeds the Array length, - // `&JsonValue::Null` will also be returned. - let no_such_index: &JsonValue = &json_value["array"][100]; - - // If you use a subscript to visit `JsonValue` types other than Object and Array, - // `&JsonValue::Null` will also be returned. - let invalid_index: &JsonValue = &json_value["key"]["invalid"]; - let invalid_index: &JsonValue = &json_value["key"][0]; -} -``` -You can also use the same method to obtain a mutable reference to `JsonValue`. -After obtaining the mutable reference, you can modify it, but you need to make sure that it conforms to JSON syntax. -```rust -use ylong_json::JsonValue; - -// JSON string for the example -const JSON_TEXT: &str = r#" -{ - "key": "value", - "array": [1, 2, 3] -} -"#; - -fn modify_key_value_pair() { - // Creates a JsonValue instance from the example string, the syntax is - // correct so the parse must succeed here, so uses unwrap. - // Here the JSON instance needs to be mutable because you need to obtain a mutable reference. - let mut json_value = JsonValue::from_str(JSON_TEXT).unwrap(); - - // Obtains a mutable reference to the member by "key" and set it to the number 123. - // In the libraty, many primitive types implement conversion from themselves to JsonValue, - // so they can be converted to `JsonValue` by using `into()` method. - // After executing this code, the contents of the table are as follows: - // { - // "key": 123, - // "array": [1, 2, 3] - // } - json_value["key"] = 123_i32.into(); - - // Obtains a mutable reference to the member by using "array" and the subscript 0, - // and set it to the number 123. - // After executing this code, the contents of the table are as follows: - // { - // "key": 123, - // "array": [123, 2, 3] - // } - json_value["array"][0] = 123_i32.into(); - - // If you try to obtain a mutable reference to a key that does not exist in the table, - // then the key will be inserted in the table with the corresponding value JsonValue::Null, - // and changes the value baesd on that. - // After executing this code, the json_value member "no_such_key" has been added, - // and the value is 123. - // The contents of the table are as follows: - // { - // "key": 123, - // "array": [123, 2, 3], - // "no_such_key": 123 - // } - json_value["no_such_key"] = 123_i32.into(); - - // When trying to obtain a mutable reference to a member of the Array type, if the - // subscript exceeds the Array length, then a `JsonValue::Null` will be added at - // the end of the Array and will return a mutable reference to that position. - // After executing this code, the length of the array member of `json_value` becomes 4, - // and the value of the last member is 123. - // The contents of the table are as follows: - // { - // "key": 123, - // "array": [123, 2, 3, 123], - // "no_such_key": 123 - // } - json_value["array"][100] = 123_i32.into(); - - // When using a subscript of &str type or String type to obtain a mutable reference to - // a non-Object type, will replace the value with an empty Object and then visit it with - // that subscript. - // After executing this code, the array member of `json_value` becomes of type Object - // and contains a key-value pair: "key" => 123. - // The contents of the table are as follows: - // { - // "key": 123, - // "array": { - // "key": 123 - // }, - // "no_such_key": 123 - // } - json_value["array"]["key"] = 123_i32.into(); - - // When using a subscript of usize type to obtain a mutable reference to a non-Array - // type, will replace the value with an empty Array and then visit it with that subscript. - // After executing this code, the key member of `json_value` becomes of type Array, - // and contains a member: key[0] => 123. - // The contents of the table are as follows: - // { - // "key": [123], - // "array": { - // "key": 123 - // }, - // "no_such_key": 123 - // } - json_value["key"][0] = 123_i32.into(); -} -``` - -### Function 3: Outputs JSON text -(1) When you have a JsonValue instance, you can convert it to text and output it to a specified location: string, file, network, etc. -```rust -use std::fs::File; -use ylong_json::JsonValue; - -fn output_json_text(json_value: JsonValue) { - // Uses `to_compact_string()` to output the `json_value` as a string. - let string = json_value.to_compact_string().unwrap(); - - // Uses `compact_encode()` to output JSON text to a specified location, - // file, io stream, etc., which implements io::Write. - let mut file: File = File::open("").unwrap(); - let _ = json_value.compact_encode(&mut file); -} -``` -Because there is no strong order requirement for JSON internal elements, -the output order of members will have a certain randomness, -but it does not affect the semantics of JSON text. - -(2) You can also serialize an instance of a type that implements the `serde::Serialize` trait to JSON text. -```rust -use std::fs::File; -use serde::Serialize; -use ylong_json::serializer_compact::{to_string, to_writer}; - -fn output_json_text(value: V) { - #[derive(Serialize)] - struct Exmaple { - int: u32, - seq: Vec<&'static str>, - tup: (i32, i32, i32), - } - - let example = Example { - int: 1, - seq: vec!["a", "b"], - tup: (1, 2, 3), - }; - - // Uses `to_string()` to output the value as a string. - let string = to_string(&example).unwrap(); - - // Uses `to_writer()` to output JSON text to a specified location, - // file, io stream, etc., which implements io::Write. - let mut file: File = File::open("./example.txt").unwrap(); - let _ = to_writer(&example, &mut file); -} +ylong_json +├─ examples # ylong_json code example +├─ include # ylong_json.h +├─ src +│ ├─ value # Array and Object type definitions and related methods +│ ├─ adapter.rs # Adapts to the C interface implementation +│ ├─ consts.rs # Some definitions of constants and tables +│ ├─ deserializer.rs # Deserialization implementation of the adaptation serde +│ ├─ encoder.rs # Serialization implementation for the `JsonValue` type +│ ├─ error.rs # Error type definition, helpful to identify the problem +│ ├─ link_list.rs # LinkedList type definition and related methods +│ ├─ serializer_compact.rs # Serialization implementation of the adaptation serde +│ ├─ states.rs # Deserialization implementation for the `JsonValue` type +│ └─ value.rs # JsonValue type definition and related methods +└─ tests # Test directory ``` ## Build @@ -284,6 +58,9 @@ deps += ["//example_path/ylong_json:lib"] ylong_json = { path = "/example_path/ylong_json" } # Uses path dependencies. ``` +## User Guide +See [user_guide](./docs/user_guide.md) + ## Performance test ``` 1.Test environment @@ -322,21 +99,3 @@ MemTotal: 16G | example4 | 18,461 ns/iter | 12,471 ns/iter | ``` -## Directory -``` -ylong_json -├─ examples # ylong_json code example -├─ include # ylong_json.h -├─ src -│ ├─ value # Array and Object type definitions and related methods -│ ├─ adapter.rs # Adapts to the C interface implementation -│ ├─ consts.rs # Some definitions of constants and tables -│ ├─ deserializer.rs # Deserialization implementation of the adaptation serde -│ ├─ encoder.rs # Serialization implementation for the `JsonValue` type -│ ├─ error.rs # Error type definition, helpful to identify the problem -│ ├─ link_list.rs # LinkedList type definition and related methods -│ ├─ serializer_compact.rs # Serialization implementation of the adaptation serde -│ ├─ states.rs # Deserialization implementation for the `JsonValue` type -│ └─ value.rs # JsonValue type definition and related methods -└─ tests # Test directory -``` \ No newline at end of file diff --git a/README_zh.md b/README_zh.md index 2455e08..f577697 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,240 +1,47 @@ # ylong_json ## 简介 - `ylong_json` 模块提供了 JSON 语法格式文本或字符串的序列化功能,以及对应生成实例的反序列化功能。 -`ylong_json` 包含以下核心功能: +### ylong_json 在 Openharmony 中的位置 +![structure](./figure/ylong_json_oh_relate.png) +以下是对于上图关键字段的描述信息: +- `ylong_json`:提供 `json` 序列化与反序列化能力的系统组件 +- `serde`:第三方库,用于高效、通用地序列化和反序列化 `Rust` 数据结构。 -### 功能一:生成 JSON 实例 -`ylong_json` 提供了从 JSON 文本或字符串生成一个 `JsonValue` 实例的功能。 +### ylong_json 内部架构图 +![structure](./figure/ylong_json_inner_structure.png) +`ylong_json` 内部主要分为两个模块,以自定义 `JsonValue` 类型为核心的模块和适配第三方库 `serde` 的模块。 -(1)可以通过以下方法创建 `JsonValue` 实例: -```rust -use std::fs::File; -use std::str::FromStr; -use std::io::Read; -use ylong_json::JsonValue; +1. `JsonValue` 是 `ylong_json` 内部自定义的结构类型,以该结构为核心构建 `json` 的序列化与反序列化功能。 +- `JsonValue` :核心结构类型,存储 `json` 内容信息,共有 6 种枚举类型变体。 +- `LinkedList`, `Vec`, `BTreeMap`:`Array` 与 `Object` 内部数据存储的三种方式,通过 `features` 选择。 +- 序列化功能:支持将 `JsonValue` 实例输出为紧凑型字符串或写到输出流中。 +- 反序列化功能:支持解析 `json` 文本或输入流中的 `json` 内容并生成 `JsonValue` 实例。 -fn create_json_value_instance() { - let str: &str = ""; - // 可以使用 from_str 接口,从 &str 类型尝试生成 JsonValue 实例。 - // 如果传入的 &str 不符合 JSON 语法,会返回对应的 Error。 - let json_value = JsonValue::from_str(str); - - let text: String = String::from(""); - // 可以使用 from_text 接口,从一系列实现 AsRef<[u8]> 的类型生成 JsonValue 实例。 - // 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。 - let json_value = JsonValue::from_text(text); - - let path: &str = ""; - // 可以使用 from_file 接口,从对应路径的文件读取内容,并尝试生成 JsonValue 实例。 - // 如果传入的 path 不合法或者文本内容不符合 JSON 语法,会返回对应的 Error。 - let json_value = JsonValue::from_file(path); - - let mut reader: Box = Box::new(File::open("").unwrap()); - // 可以使用 from_reader 接口,从实现了 io::Read 的实例中读取文本,并尝试生成 JsonValue 实例。 - // 如果读取失败或者从 reader 中读取的内容不符合 JSON 语法,会返回对应的 Error。 - let json_value = JsonValue::from_reader(&mut reader); -} +2. `ylong_json` 适配了第三方库 `serde` +- `Serializer`:序列化输出的辅助结构类型。 +- `Deserializer`:反序列化输出的辅助结构类型。 +- 序列化功能:支持将实现了 `serde::Serialize` trait 的类型实例序列化为 `json` 文本内容或将内容写到输出流中。 +- 反序列化功能:若实现了 `serde::Deserialize` trait 的类型在 `json` 内容中,则可将该部分 `json` 内容反序列化为该类型的实例。 + +## 目录 ``` -当 `JsonValue` 实例创建成功后,就可以尝试读取和修改对应的内容了。 - -(2)如果 JSON 文本中的类型实现了第三方库 `serde::Deserialize` trait,则可以直接将文本内容反序列化为该类型的实例。 -```rust -use std::fs::File; -use serde::Deserialize; -use ylong_json::deserializer::{from_reader, from_slice, from_st}; -fn deserialize_json_to_instance() { - #[derive(Deserialize, PartialEq, Debug)] - struct Example { - int: u32, - seq: Vec, - tup: (i32, i32, i32), - } - - // 可以使用 from_str 接口,从 &str 类型生成实例。 - // 如果传入的 &str 不符合 JSON 语法,会返回对应的 Error。 - let tr = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; - let example = from_str::(str).unwrap(); - - // 可以使用 from_slice 接口,从 &u8 类型生成实例。 - // 如果传入的 &u8 不符合 JSON 语法,会返回对应的 Error。 - let slice = str.as_bytes(); - let example = from_slice::(slice).unwrap(); - - - // 可以使用 from_reader 接口,从实现了 io::Write 的位置、文件、io流等生成实例。 - // 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。 - let mut file: File = File::open("./example.txt").unwrap(); - let example = from_reader::(file).unwrap(); -} -``` - -### 功能二:读取、修改键值对 -`JsonValue` 实例生成成功后,可以通过各种下标来查找对应的键值对(获取到对应 `JsonValue` 的普通引用)。 - -&str 和 String 类型的下标可以用于查找 Object 内的键值对;usize 类型的下标可以用于查找 Array 内的键值对。 -```rust -use std::str::FromStr; -use ylong_json::JsonValue; - -// 示例的 JSON 字符串 -const JSON_TEXT: &str = r#" -{ - "key": "value", - "array": [1, 2, 3] -} -"#; - -fn find_key_value_pair() { - // 根据示例字符串创建 JsonValue 实例,语法正确所以此处解析必定成功,使用 unwrap。 - let json_value = JsonValue::from_str(JSON_TEXT).unwrap(); - - // 由于 json 本身也是一个表,所以可以使用 &str 类型获取内部值的普通引用。 - let value: &JsonValue = &json_value["key"]; - - // 可以通过 &str 类型先获取到 “array” 成员的普通引用,再根据 usize 类型获取对应元素的普通引用。 - let array_item: &JsonValue = &json_value["array"][0]; - - // 如果尝试查找一个不存在表中的键,会返回 &JsonValue::Null。 - let no_such_key: &JsonValue = &json_value["no_such_key"]; - - // 对 Array 类型查找时,若下标超过 Array 长度,也会返回 &JsonValue::Null。 - let no_such_index: &JsonValue = &json_value["array"][100]; - - // 对一个 Object 和 Array 类型以外的 JsonValue 类型使用下标访问也会返回 &JsonValue::Null。 - let invalid_index: &JsonValue = &json_value["key"]["invalid"]; - let invalid_index: &JsonValue = &json_value["key"][0]; -} -``` -也可以通过相同方法获取到对应 `JsonValue` 的可变引用,获取到可变引用后可以对其进行修改,修改时需要注意符合 JSON 语法。 -```rust -use ylong_json::JsonValue; - -// 示例的 JSON 字符串 -const JSON_TEXT: &str = r#" -{ - "key": "value", - "array": [1, 2, 3] -} -"#; - -fn modify_key_value_pair() { - // 根据示例字符串创建 JsonValue 实例,语法正确所以此处解析必定成功,使用 unwrap。 - // 此处由于需要获取可变引用,JSON 实例需要可变。 - let mut json_value = JsonValue::from_str(JSON_TEXT).unwrap(); - - // 通过 “key” 获取到对应成员的可变引用,并将其设置为数值 123。 - // 库中给许多基本类型实现了从自身到 JsonValue 的转换,所以可以通过 into() 方法转换为 JsonValue。 - // 执行此句代码后,表中内容如下: - // { - // "key": 123, - // "array": [1, 2, 3] - // } - json_value["key"] = 123_i32.into(); - - // 通过 “array” 和下标 0 获取到对应成员的可变引用,并将其设置为数值 123。 - // 执行此句代码后,表中内容如下: - // { - // "key": 123, - // "array": [123, 2, 3] - // } - json_value["array"][0] = 123_i32.into(); - - // 如果尝试获取一个不存在表中的键的可变引用,会在表中插入该键且对应值为 JsonValue::Null,并在此基础上进行修改。 - // 执行此行代码后,json_value 中会增加一个成员 “no_such_key”,且值为数值 123。 - // 表中内容如下: - // { - // "key": 123, - // "array": [123, 2, 3], - // "no_such_key": 123 - // } - json_value["no_such_key"] = 123_i32.into(); - - // 对 Array 类型的成员尝试获取可变引用时,若下标超过 Array 长度, - // 会在 Array 末尾插入一个 JsonValue::Null,并返回该位置的可变引用。 - // 执行此行代码后,json_value 的 “array” 成员的长度变为 4。 - // 表中内容如下: - // { - // "key": 123, - // "array": [123, 2, 3, 123], - // "no_such_key": 123 - // } - json_value["array"][100] = 123_i32.into(); - - // 对一个非 Object 类型使用 &str 类型或 String 下标获取可变引用时, - // 会将该值替换为一个空 Object,然后再用此下标对其进行访问。 - // 执行此代码后,json_value 的 array 成员变成 Object 类型,且含有一个键值对:“key” => 123。 - // 表中内容如下: - // { - // "key": 123, - // "array": { - // "key": 123 - // }, - // "no_such_key": 123 - // } - json_value["array"]["key"] = 123_i32.into(); - - // 对一个非 Array 类型使用 usize 类型下标获取可变引用时, - // 会将该值替换成一个空 Array,然后再用此下标对其进行访问。 - // 执行此代码后,json_value 的 key 成员变成 Array 类型,且含有一个成员: key[0] => 123 - // 表中内容如下: - // { - // "key": [123], - // "array": { - // "key": 123 - // }, - // "no_such_key": 123 - // } - json_value["key"][0] = 123_i32.into(); -} -``` - -### 功能三:输出 JSON 文本 -(1)当拥有一个 `JsonValue` 实例时,可以将该 `JsonValue` 实例转化成文本并输出到指定位置:字符串、文件、网络等。 -```rust -use std::fs::File; -use ylong_json::JsonValue; - -fn output_json_text(json_value: JsonValue) { - // 使用 to_compact_string() 接口将 json_value 输出成一个字符串。 - let string = json_value.to_compact_string().unwrap(); - - // 使用 compact_encode() 接口将 JSON 文本输出到指定实现了 io::Write 的位置,文件、io流等。 - let mut file: File = File::open("").unwrap(); - let _ = json_value.compact_encode(&mut file); -} -``` -由于 JSON 内部元素没有较强的顺序要求,所以成员的输出顺序会有一定随机性,但是不影响 JSON 文本的语义。 - -(2)可以将一个实现了第三方库 `serde::Serialize` trait 的类型实例序列化为 JSON 文本。 -```rust -use std::fs::File; -use serde::Serialize; -use ylong_json::serializer_compact::{to_string, to_writer}; - -fn output_json_text() { - #[derive(Serialize)] - struct Exmaple { - int: u32, - seq: Vec<&'static str>, - tup: (i32, i32, i32), - } - - let example = Example { - int: 1, - seq: vec!["a", "b"], - tup: (1, 2, 3), - }; - - // 使用 to_string() 接口将 value 输出成一个字符串。 - let string = to_string(&example).unwrap(); - - // 使用 to_writer() 接口将 JSON 文本输出到指定实现了 io::Write 的位置,文件、io流等。 - let mut file: File = File::open("./example.txt").unwrap(); - let _ = to_writer(&example, &mut file); -} +ylong_json +├─ examples # ylong_json 代码示例 +├─ include # ylong_json.h +├─ src +│ ├─ value # Array, Object 类型定义和相关方法实现 +│ ├─ adapter.rs # 适配 C 的接口实现 +│ ├─ consts.rs # 一些常数与表格的定义 +│ ├─ deserializer.rs # 适配 serde 的反序列化实现 +│ ├─ encoder.rs # 为 JsonValue 类型序列化实现 +│ ├─ error.rs # 错误类型定义,便于定位 +│ ├─ link_list.rs # LinkedList 类型定义和相关方法实现 +│ ├─ serializer_compact.rs # 适配 serde 的序列化实现 +│ ├─ states.rs # 为 JsonValue 类型反序列化实现 +│ └─ value.rs # JsonValue 类型定义和相关方法实现 +└─ tests # 测试目录 ``` ## 编译构建 @@ -251,6 +58,9 @@ deps += ["//example_path/ylong_json:lib"] ylong_json = { path = "/example_path/ylong_json" } # 请使用路径依赖 ``` +## 用户指南 +详情内容请见[用户指南](./docs/user_guide_zh.md) + ## 性能测试 ``` 1.测试环境 @@ -288,23 +98,3 @@ CPU 核心数:8 | example3 | 55,910 ns/iter | 59,717 ns/iter | | example4 | 18,461 ns/iter | 12,471 ns/iter | ``` - -## 目录 - -``` -ylong_json -├─ examples # ylong_json 代码示例 -├─ include # ylong_json.h -├─ src -│ ├─ value # Array, Object 类型定义和相关方法实现 -│ ├─ adapter.rs # 适配 C 的接口实现 -│ ├─ consts.rs # 一些常数与表格的定义 -│ ├─ deserializer.rs # 适配 serde 的反序列化实现 -│ ├─ encoder.rs # 为 JsonValue 类型序列化实现 -│ ├─ error.rs # 错误类型定义,便于定位 -│ ├─ link_list.rs # LinkedList 类型定义和相关方法实现 -│ ├─ serializer_compact.rs # 适配 serde 的序列化实现 -│ ├─ states.rs # 为 JsonValue 类型反序列化实现 -│ └─ value.rs # JsonValue 类型定义和相关方法实现 -└─ tests # 测试目录 -``` \ No newline at end of file diff --git a/RELEASE_NOTE.md b/RELEASE_NOTE.md new file mode 100644 index 0000000..40bd359 --- /dev/null +++ b/RELEASE_NOTE.md @@ -0,0 +1,5 @@ +# 1.0.0 +1. ylong_json initial version, which provides these features: + - serialize: output JSON text or string from `JsonValue`. + - deserialize: generate an instance of `JsonValue` from JSON text or string. + - serde: serialize or deserialize for a struct which implements the serde trait \ No newline at end of file diff --git a/docs/user_guide.md b/docs/user_guide.md new file mode 100644 index 0000000..c4ea8c9 --- /dev/null +++ b/docs/user_guide.md @@ -0,0 +1,269 @@ +# ylong_json User Guide + +The `ylong_json` module provides serialization of text or string in JSON syntax format, and deserialization of corresponding generated instances. In addition, the `ylong_json` module also adapts to the third-party library `serde`. Structures that implement specific traits in the `serde` library can be serialized and deserialized. + +If you need to see the detailed interface description, please check the docs of the corresponding interface. You can use `cargo doc --open` to generate and view the docs. + +### Function 1: Generates a JSON instance +`ylong_json` provides the ability to generate an instance of `JsonValue` from JSON text or string. You need to use a series of instance creation methods for the "JsonValue" to use this feature. + +(1) You can create a `JsonValue` instance by: +```rust +use std::fs::File; +use std::str::FromStr; +use std::io::Read; +use ylong_json::JsonValue; +fn create_json_value_instance() { + let str: &str = ""; + // You can use `from_str` to try to generate a `JsonValue` instance from + // the &str type. + // If the passed &str does not conform to JSON syntax, the corresponding + // Error will be returned. + let json_value = JsonValue::from_str(str); + + let text: String = String::from(""); + // You can use `from_text` to generate a `JsonValue` instance from + // a series of types that implement AsRef<[u8]>. + // If the passed text content does not conform to JSON syntax, the + // corresponding Error will be returned. + let json_value = JsonValue::from_text(text); + + let path: &str = ""; + // You can use `from_file` to read a file from corresponding path and + // try to generate a `JsonValue` instance. + // If the passed path is not valid or the text content does not conform + // to JSON syntax, the corresponding Error will be returned. + let json_value = JsonValue::from_file(path); + + let mut reader: Box = Box::new(File::open("").unwrap()); + // You can use `from_reader` interface to read text from an instance + // that implements io::Read and try to generate a `JsonValue` instance. + // If the read fails or if the content from the reader does not conform + // to JSON syntax, the corresponding Error will be returned. + let json_value = JsonValue::from_reader(&mut reader); +} +``` +Once the `JsonValue` instance has been successfully created, you can attempt to read and modify the corresponding contents. + +(2) If the type in the JSON text implements the third-party library `serde::Deserialize` trait, you can directly deserialize the text content to an instance of that type. +```rust +use std::fs::File; +use serde::Deserialize; +use ylong_json::deserializer::{from_reader, from_slice, from_st}; +fn deserialize_json_to_instance() { + #[derive(Deserialize, PartialEq, Debug)] + struct Example { + int: u32, + seq: Vec, + tup: (i32, i32, i32), + } + + // You can use `from_str` to try to generate an instance from String. + // If the passed String does not conform to JSON syntax, the corresponding + // Error will be returned. + let str = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; + let example = from_str::(str).unwrap(); + + // You can use `from_slice` to try to generate an instance from &u8. + // If the passed &u8 does not conform to JSON syntax, the corresponding + // Error will be returned. + let slice = str.as_bytes(); + let example = from_slice::(slice).unwrap(); + + // You can use `from_reader` to try to generate an instance from + // locations, files, io streams, and so on that implement io::Write. + // If the passed text content does not conform to JSON syntax, + // the corresponding Error will be returned. + let mut file: File = File::open("./example.txt").unwrap(); + let example = from_reader::(file).unwrap(); +} +``` + +### Function 2: Reads and modifies a key-value pair +After a `JsonValue` instance is successfully generated, you can use a subscript to find the corresponding key-value pair (to obtain a common reference to the corresponding `JsonValue`). + +A subscript of type &str or String can be used to find a key-value pair in Object; +A Subscript of type usize can be used to find a key-value pair in an Array. +```rust +use std::str::FromStr; +use ylong_json::JsonValue; + +// JSON string for the example +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn find_key_value_pair() { + // Creates a JsonValue instance from the example string, the syntax is + // correct so the parse must succeed here, so uses unwrap. + let json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // Since json is itself a table, you can use the &str type to obtain + // a common reference to the internal value. + let value: &JsonValue = &json_value["key"]; + + // You can use the &str type to obtain a common reference to the "array" member, and + // then use the usize type to obtain a common reference to the corresponding element. + let array_item: &JsonValue = &json_value["array"][0]; + + // If you try to find a key that does not exist in a table, + // `&JsonValue::Null` will be returned. + let no_such_key: &JsonValue = &json_value["no_such_key"]; + + // When searching for the Array type, if the subscript exceeds the Array length, + // `&JsonValue::Null` will also be returned. + let no_such_index: &JsonValue = &json_value["array"][100]; + + // If you use a subscript to visit `JsonValue` types other than Object and Array, + // `&JsonValue::Null` will also be returned. + let invalid_index: &JsonValue = &json_value["key"]["invalid"]; + let invalid_index: &JsonValue = &json_value["key"][0]; +} +``` +You can also use the same method to obtain a mutable reference to `JsonValue`. +After obtaining the mutable reference, you can modify it, but you need to make sure that it conforms to JSON syntax. +```rust +use ylong_json::JsonValue; + +// JSON string for the example +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn modify_key_value_pair() { + // Creates a JsonValue instance from the example string, the syntax is + // correct so the parse must succeed here, so uses unwrap. + // Here the JSON instance needs to be mutable because you need to obtain a mutable reference. + let mut json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // Obtains a mutable reference to the member by "key" and set it to the number 123. + // In the libraty, many primitive types implement conversion from themselves to JsonValue, + // so they can be converted to `JsonValue` by using `into()` method. + // After executing this code, the contents of the table are as follows: + // { + // "key": 123, + // "array": [1, 2, 3] + // } + json_value["key"] = 123_i32.into(); + + // Obtains a mutable reference to the member by using "array" and the subscript 0, + // and set it to the number 123. + // After executing this code, the contents of the table are as follows: + // { + // "key": 123, + // "array": [123, 2, 3] + // } + json_value["array"][0] = 123_i32.into(); + + // If you try to obtain a mutable reference to a key that does not exist in the table, + // then the key will be inserted in the table with the corresponding value JsonValue::Null, + // and changes the value baesd on that. + // After executing this code, the json_value member "no_such_key" has been added, + // and the value is 123. + // The contents of the table are as follows: + // { + // "key": 123, + // "array": [123, 2, 3], + // "no_such_key": 123 + // } + json_value["no_such_key"] = 123_i32.into(); + + // When trying to obtain a mutable reference to a member of the Array type, if the + // subscript exceeds the Array length, then a `JsonValue::Null` will be added at + // the end of the Array and will return a mutable reference to that position. + // After executing this code, the length of the array member of `json_value` becomes 4, + // and the value of the last member is 123. + // The contents of the table are as follows: + // { + // "key": 123, + // "array": [123, 2, 3, 123], + // "no_such_key": 123 + // } + json_value["array"][100] = 123_i32.into(); + + // When using a subscript of &str type or String type to obtain a mutable reference to + // a non-Object type, will replace the value with an empty Object and then visit it with + // that subscript. + // After executing this code, the array member of `json_value` becomes of type Object + // and contains a key-value pair: "key" => 123. + // The contents of the table are as follows: + // { + // "key": 123, + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["array"]["key"] = 123_i32.into(); + + // When using a subscript of usize type to obtain a mutable reference to a non-Array + // type, will replace the value with an empty Array and then visit it with that subscript. + // After executing this code, the key member of `json_value` becomes of type Array, + // and contains a member: key[0] => 123. + // The contents of the table are as follows: + // { + // "key": [123], + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["key"][0] = 123_i32.into(); +} +``` + +### Function 3: Outputs JSON text +(1) When you have a JsonValue instance, you can convert it to text and output it to a specified location: string, file, network, etc. +```rust +use std::fs::File; +use ylong_json::JsonValue; + +fn output_json_text(json_value: JsonValue) { + // Uses `to_compact_string()` to output the `json_value` as a string. + let string = json_value.to_compact_string().unwrap(); + + // Uses `compact_encode()` to output JSON text to a specified location, + // file, io stream, etc., which implements io::Write. + let mut file: File = File::open("").unwrap(); + let _ = json_value.compact_encode(&mut file); +} +``` +Because there is no strong order requirement for JSON internal elements, +the output order of members will have a certain randomness, +but it does not affect the semantics of JSON text. + +(2) You can also serialize an instance of a type that implements the `serde::Serialize` trait to JSON text. +```rust +use std::fs::File; +use serde::Serialize; +use ylong_json::serializer_compact::{to_string, to_writer}; + +fn output_json_text(value: V) { + #[derive(Serialize)] + struct Exmaple { + int: u32, + seq: Vec<&'static str>, + tup: (i32, i32, i32), + } + + let example = Example { + int: 1, + seq: vec!["a", "b"], + tup: (1, 2, 3), + }; + + // Uses `to_string()` to output the value as a string. + let string = to_string(&example).unwrap(); + + // Uses `to_writer()` to output JSON text to a specified location, + // file, io stream, etc., which implements io::Write. + let mut file: File = File::open("./example.txt").unwrap(); + let _ = to_writer(&example, &mut file); +} +``` \ No newline at end of file diff --git a/docs/user_guide_zh.md b/docs/user_guide_zh.md new file mode 100644 index 0000000..a3b8052 --- /dev/null +++ b/docs/user_guide_zh.md @@ -0,0 +1,235 @@ +# ylong_json 用户指南 + +`ylong_json` 模块提供了 JSON 语法格式文本或字符串的序列化功能,以及对应生成实例的反序列化功能。此外,`ylong_json`模块还适配了第三方库 `serde`。对于实现了 `serde` 库中特定 trait 的结构,可以对其进行序列化与反序列化操作。 + +如果需要查看详细的接口说明请查看对应接口的 docs,可以使用 `cargo doc --open` 生成并查看 docs。 + +## 功能一:生成 JSON 实例 + +(1)可以使用 `JsonValue` 所具有的四种方法从不同的来源生成 `JsonValue` 实例: +```rust +use std::fs::File; +use std::str::FromStr; +use std::io::Read; +use ylong_json::JsonValue; + +fn create_json_value_instance() { + let str: &str = ""; + // 可以使用 from_str 接口,从 &str 类型尝试生成 JsonValue 实例。 + // 如果传入的 &str 不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_str(str); + + let text: String = String::from(""); + // 可以使用 from_text 接口,从一系列实现 AsRef<[u8]> 的类型生成 JsonValue 实例。 + // 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_text(text); + + let path: &str = ""; + // 可以使用 from_file 接口,从对应路径的文件读取内容,并尝试生成 JsonValue 实例。 + // 如果传入的 path 不合法或者文本内容不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_file(path); + + let mut reader: Box = Box::new(File::open("").unwrap()); + // 可以使用 from_reader 接口,从实现了 io::Read 的实例中读取文本,并尝试生成 JsonValue 实例。 + // 如果读取失败或者从 reader 中读取的内容不符合 JSON 语法,会返回对应的 Error。 + let json_value = JsonValue::from_reader(&mut reader); +} +``` +当 `JsonValue` 实例创建成功后,就可以尝试读取和修改对应的内容了。 + +(2)如果 JSON 文本中的类型实现了第三方库 `serde::Deserialize` trait,则可以直接将文本内容反序列化为该类型的实例。 +```rust +use std::fs::File; +use serde::Deserialize; +use ylong_json::deserializer::{from_reader, from_slice, from_st}; +fn deserialize_json_to_instance() { + #[derive(Deserialize, PartialEq, Debug)] + struct Example { + int: u32, + seq: Vec, + tup: (i32, i32, i32), + } + + // 可以使用 from_str 接口,从 &str 类型生成实例。 + // 如果传入的 &str 不符合 JSON 语法,会返回对应的 Error。 + let tr = r#"{"int":1,"seq":["abcd","efgh"],"tup":[1,2,3]}"#; + let example = from_str::(str).unwrap(); + + // 可以使用 from_slice 接口,从 &u8 类型生成实例。 + // 如果传入的 &u8 不符合 JSON 语法,会返回对应的 Error。 + let slice = str.as_bytes(); + let example = from_slice::(slice).unwrap(); + + + // 可以使用 from_reader 接口,从实现了 io::Write 的位置、文件、io流等生成实例。 + // 如果传入的文本内容不符合 JSON 语法,会返回对应的 Error。 + let mut file: File = File::open("./example.txt").unwrap(); + let example = from_reader::(file).unwrap(); +} +``` + +### 功能二:读取、修改键值对 +`JsonValue` 实例生成成功后,可以通过各种下标来查找对应的键值对(获取到对应 `JsonValue` 的普通引用)。 + +&str 和 String 类型的下标可以用于查找 Object 内的键值对;usize 类型的下标可以用于查找 Array 内的键值对。 +```rust +use std::str::FromStr; +use ylong_json::JsonValue; + +// 示例的 JSON 字符串 +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn find_key_value_pair() { + // 根据示例字符串创建 JsonValue 实例,语法正确所以此处解析必定成功,使用 unwrap。 + let json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // 由于 json 本身也是一个表,所以可以使用 &str 类型获取内部值的普通引用。 + let value: &JsonValue = &json_value["key"]; + + // 可以通过 &str 类型先获取到 “array” 成员的普通引用,再根据 usize 类型获取对应元素的普通引用。 + let array_item: &JsonValue = &json_value["array"][0]; + + // 如果尝试查找一个不存在表中的键,会返回 &JsonValue::Null。 + let no_such_key: &JsonValue = &json_value["no_such_key"]; + + // 对 Array 类型查找时,若下标超过 Array 长度,也会返回 &JsonValue::Null。 + let no_such_index: &JsonValue = &json_value["array"][100]; + + // 对一个 Object 和 Array 类型以外的 JsonValue 类型使用下标访问也会返回 &JsonValue::Null。 + let invalid_index: &JsonValue = &json_value["key"]["invalid"]; + let invalid_index: &JsonValue = &json_value["key"][0]; +} +``` +也可以通过相同方法获取到对应 `JsonValue` 的可变引用,获取到可变引用后可以对其进行修改,修改时需要注意符合 JSON 语法。 +```rust +use ylong_json::JsonValue; + +// 示例的 JSON 字符串 +const JSON_TEXT: &str = r#" +{ + "key": "value", + "array": [1, 2, 3] +} +"#; + +fn modify_key_value_pair() { + // 根据示例字符串创建 JsonValue 实例,语法正确所以此处解析必定成功,使用 unwrap。 + // 此处由于需要获取可变引用,JSON 实例需要可变。 + let mut json_value = JsonValue::from_str(JSON_TEXT).unwrap(); + + // 通过 “key” 获取到对应成员的可变引用,并将其设置为数值 123。 + // 库中给许多基本类型实现了从自身到 JsonValue 的转换,所以可以通过 into() 方法转换为 JsonValue。 + // 执行此句代码后,表中内容如下: + // { + // "key": 123, + // "array": [1, 2, 3] + // } + json_value["key"] = 123_i32.into(); + + // 通过 “array” 和下标 0 获取到对应成员的可变引用,并将其设置为数值 123。 + // 执行此句代码后,表中内容如下: + // { + // "key": 123, + // "array": [123, 2, 3] + // } + json_value["array"][0] = 123_i32.into(); + + // 如果尝试获取一个不存在表中的键的可变引用,会在表中插入该键且对应值为 JsonValue::Null,并在此基础上进行修改。 + // 执行此行代码后,json_value 中会增加一个成员 “no_such_key”,且值为数值 123。 + // 表中内容如下: + // { + // "key": 123, + // "array": [123, 2, 3], + // "no_such_key": 123 + // } + json_value["no_such_key"] = 123_i32.into(); + + // 对 Array 类型的成员尝试获取可变引用时,若下标超过 Array 长度, + // 会在 Array 末尾插入一个 JsonValue::Null,并返回该位置的可变引用。 + // 执行此行代码后,json_value 的 “array” 成员的长度变为 4。 + // 表中内容如下: + // { + // "key": 123, + // "array": [123, 2, 3, 123], + // "no_such_key": 123 + // } + json_value["array"][100] = 123_i32.into(); + + // 对一个非 Object 类型使用 &str 类型或 String 下标获取可变引用时, + // 会将该值替换为一个空 Object,然后再用此下标对其进行访问。 + // 执行此代码后,json_value 的 array 成员变成 Object 类型,且含有一个键值对:“key” => 123。 + // 表中内容如下: + // { + // "key": 123, + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["array"]["key"] = 123_i32.into(); + + // 对一个非 Array 类型使用 usize 类型下标获取可变引用时, + // 会将该值替换成一个空 Array,然后再用此下标对其进行访问。 + // 执行此代码后,json_value 的 key 成员变成 Array 类型,且含有一个成员: key[0] => 123 + // 表中内容如下: + // { + // "key": [123], + // "array": { + // "key": 123 + // }, + // "no_such_key": 123 + // } + json_value["key"][0] = 123_i32.into(); +} +``` + +### 功能三:输出 JSON 文本 +(1)当拥有一个 `JsonValue` 实例时,可以将该 `JsonValue` 实例转化成文本并输出到指定位置:字符串、文件、网络等。 +```rust +use std::fs::File; +use ylong_json::JsonValue; + +fn output_json_text(json_value: JsonValue) { + // 使用 to_compact_string() 接口将 json_value 输出成一个字符串。 + let string = json_value.to_compact_string().unwrap(); + + // 使用 compact_encode() 接口将 JSON 文本输出到指定实现了 io::Write 的位置,文件、io流等。 + let mut file: File = File::open("").unwrap(); + let _ = json_value.compact_encode(&mut file); +} +``` +由于 JSON 内部元素没有较强的顺序要求,所以成员的输出顺序会有一定随机性,但是不影响 JSON 文本的语义。 + +(2)可以将一个实现了第三方库 `serde::Serialize` trait 的类型实例序列化为 JSON 文本。 +```rust +use std::fs::File; +use serde::Serialize; +use ylong_json::serializer_compact::{to_string, to_writer}; + +fn output_json_text() { + #[derive(Serialize)] + struct Exmaple { + int: u32, + seq: Vec<&'static str>, + tup: (i32, i32, i32), + } + + let example = Example { + int: 1, + seq: vec!["a", "b"], + tup: (1, 2, 3), + }; + + // 使用 to_string() 接口将 value 输出成一个字符串。 + let string = to_string(&example).unwrap(); + + // 使用 to_writer() 接口将 JSON 文本输出到指定实现了 io::Write 的位置,文件、io流等。 + let mut file: File = File::open("./example.txt").unwrap(); + let _ = to_writer(&example, &mut file); +} +``` \ No newline at end of file diff --git a/figure/ylong_json_inner_structure.png b/figure/ylong_json_inner_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..9d526afd9d62a4d608d82b81ef0be766ab29052d GIT binary patch literal 21764 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%0DBRrGywv5|S8ICMsi|g}2^ps6U4h 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 MJspiA_2+;76ZT1Ik^lez literal 0 HcmV?d00001