Signed-off-by: 薛磊 <xuelei3@huawei.com>
13 KiB
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:
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:
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<dyn Read> = 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.
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<String>,
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::<Example>(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::<Example>(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::<Example>(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.
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.
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.
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.
use std::fs::File;
use serde::Serialize;
use ylong_json::serializer_compact::{to_string, to_writer};
fn<V: Serialize> 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);
}
Performance test
1.Test environment
OS: Linux
Architecture: x86_64
Byte Order: Little Endian
Model number: Intel(R) Xeon(R) Gold 6278C CPU @ 2.60GHz
CPU(s): 8
MemTotal: 16G
2.Test result
| Serialize | ylong_json | serde_json |
------------------------------------------------
| null | 150 ns/iter | 175 ns/iter |
| boolean | 155 ns/iter | 178 ns/iter |
| number | 309 ns/iter | 291 ns/iter |
| string | 513 ns/iter | 413 ns/iter |
| array | 998 ns/iter | 1,075 ns/iter |
| object | 1,333 ns/iter | 1,348 ns/iter |
| example1 | 12,537 ns/iter | 12,288 ns/iter |
| example2 | 23,754 ns/iter | 21,936 ns/iter |
| example3 | 103,061 ns/iter | 97,247 ns/iter |
| example4 | 15,234 ns/iter | 17,895 ns/iter |
| Deserialize | ylong_json | serde_json |
--------------------------------------------------
| null | 257 ns/iter | 399 ns/iter |
| boolean | 260 ns/iter | 400 ns/iter |
| number | 1,507 ns/iter | 989 ns/iter |
| string | 414 ns/iter | 610 ns/iter |
| array | 2,258 ns/iter | 2,148 ns/iter |
| object | 810 ns/iter | 1,386 ns/iter |
| example1 | 10,191 ns/iter | 10,227 ns/iter |
| example2 | 15,753 ns/iter | 18,022 ns/iter |
| example3 | 55,910 ns/iter | 59,717 ns/iter |
| 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