├── .github └── workflows │ ├── benchmark.yml │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── ajson_benchmark.rs ├── nom_json.rs └── traversing.rs ├── clippy.toml ├── rustfmt.toml ├── src ├── element.rs ├── lib.rs ├── number.rs ├── parser.rs ├── path │ ├── builder.rs │ ├── mod.rs │ ├── parser.rs │ ├── query.rs │ └── sub_selector.rs ├── unescape.rs ├── util.rs ├── value.rs └── wild.rs └── tests └── test_ajson.rs /.github/workflows/benchmark.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test-in-stable: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Install latest stable 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: stable 23 | override: true 24 | components: rustfmt, clippy 25 | - run: cargo bench --bench ajson_benchmark 26 | 27 | test-in-nightly: 28 | 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | - name: Install latest nightly 34 | uses: actions-rs/toolchain@v1 35 | with: 36 | toolchain: nightly 37 | override: true 38 | components: rustfmt, clippy 39 | - run: cargo bench --bench ajson_benchmark 40 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test-in-stable: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Install latest stable 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: stable 23 | override: true 24 | components: rustfmt, clippy 25 | - run: cargo test --features=wild --verbose 26 | 27 | test-in-nightly: 28 | 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | - name: Install latest nightly 34 | uses: actions-rs/toolchain@v1 35 | with: 36 | toolchain: nightly 37 | override: true 38 | components: rustfmt, clippy 39 | - run: cargo test --features=wild --verbose 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | perf.data 5 | perf.data.old 6 | flamegraph.svg -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ajson" 3 | edition = "2018" 4 | version = "0.3.1" 5 | authors = ["importcjj "] 6 | license = "MIT" 7 | description = "JSON Parser for Rust - Get JSON values quickly" 8 | keywords = ["json", "json-parser", "gjson", "ajson"] 9 | categories = ["encoding"] 10 | readme = "README.md" 11 | documentation = "https://docs.rs/ajson/latest/ajson/" 12 | repository = "https://github.com/importcjj/rust-ajson" 13 | include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-MIT"] 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | autobenches = false 16 | 17 | [lib] 18 | bench = false 19 | 20 | [features] 21 | default = [] 22 | wild = ["regex"] 23 | 24 | [dependencies] 25 | regex = { version = "1", optional = true } 26 | smallvec = "1.9.0" 27 | 28 | [dev-dependencies] 29 | criterion = "0.2" 30 | json = "0.11.13" 31 | serde_json = "1.0" 32 | serde = { version = "1.0.98", features = ["derive"] } 33 | nom = "5" 34 | gjson = "0.8.1" 35 | 36 | [[bench]] 37 | name = "ajson_benchmark" 38 | path = "benches/ajson_benchmark.rs" 39 | harness = false 40 | 41 | [[bench]] 42 | name = "traversing_benchmark" 43 | path = "benches/traversing.rs" 44 | harness = false 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Importcjj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

A-JSON

4 |

Read JSON values quickly - Rust JSON Parser

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | #### change name to AJSON, see [issue](https://github.com/importcjj/a-json/issues/2) 22 | Inspiration comes from [gjson](https://github.com/tidwall/gjson) in golang 23 | 24 | ## Installation 25 | Add it to your `Cargo.toml` file: 26 | ``` 27 | [dependencies] 28 | ajson = "0.3" 29 | ``` 30 | 31 | ## Todo 32 | 33 | * Add documentation 34 | * Follow api-guidelines 35 | * Update benchmark 36 | * Optimize 37 | 38 | ## A simple example 39 | 40 | AJSON get json value with specified path, such as `project.name` or `project.version`. When the path matches, it returns immediately! 41 | 42 | ```rust 43 | let data = r#" 44 | { 45 | "project": { 46 | "name": "ajson", 47 | "maintainer": "importcjj", 48 | "version": 0.1, 49 | "rusts": ["stable", "nightly"] 50 | } 51 | } 52 | "#; 53 | 54 | let name = ajson::get(data, "project.name").unwrap().unwrap(); 55 | println!("{}", name.as_str()); // ajson 56 | ``` 57 | 58 | ## Path Syntax 59 | 60 | JSON example 61 | 62 | ```json 63 | { 64 | "name": {"first": "Tom", "last": "Anderson"}, 65 | "age":37, 66 | "children": ["Sara","Alex","Jack"], 67 | "fav.movie": "Deer Hunter", 68 | "friends": [ 69 | {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, 70 | {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, 71 | {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} 72 | ] 73 | } 74 | ``` 75 | 76 | #### basic 77 | Below is a quick overview of the path syntax, for more complete information please check out GJSON Syntax. 78 | 79 | A path is a series of keys separated by a dot. A key may contain special wildcard characters '*' and '?'. To access an array value use the index as the key. To get the number of elements in an array or to access a child path, use the '#' character. The dot and wildcard characters can be escaped with '\'. 80 | 81 | ``` 82 | name.last >> "Anderson" 83 | age >> 37 84 | children >> ["Sara","Alex","Jack"] 85 | children.# >> 3 86 | children.1 >> "Alex" 87 | child*.2 >> "Jack" 88 | c?ildren.0 >> "Sara" 89 | fav\.movie >> "Deer Hunter" 90 | friends.#.first >> ["Dale","Roger","Jane"] 91 | friends.1.last >> "Craig" 92 | ``` 93 | 94 | #### Escape character 95 | Special purpose characters, such as ., *, and ? can be escaped with \. 96 | 97 | ``` 98 | fav\.movie "Deer Hunter" 99 | ``` 100 | 101 | #### Arrays 102 | The # character allows for digging into JSON Arrays.To get the length of an array you'll just use the # all by itself. 103 | 104 | ``` 105 | friends.# 3 106 | friends.#.age [44,68,47] 107 | ``` 108 | 109 | #### queries 110 | You can also query an array for the first match by using #(...), or find all matches with #(...)#. Queries support the ==, !=, <, <=, >, >= comparison operators and the simple pattern matching % (like) and !% (not like) operators. 111 | 112 | ``` 113 | friends.#(last=="Murphy").first >> "Dale" 114 | friends.#(last=="Murphy")#.first >> ["Dale","Jane"] 115 | friends.#(age>45)#.last >> ["Craig","Murphy"] 116 | friends.#(first%"D*").last >> "Murphy" 117 | friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] 118 | ``` 119 | 120 | #### construct 121 | Basically, you can use selectors to assemble whatever you want, and of course, the result is still a json ;) 122 | 123 | 124 | ``` 125 | {name.first,age,"murphys":friends.#(last="Murphy")#.first} 126 | [name.first,age,children.0] 127 | ``` 128 | 129 | ```rust 130 | ajson::get(json, "name.[first,last]").unwrap().unwrap().to_vec(); 131 | ajson::get(json, "name.first").unwrap().unwrap(); 132 | ajson::get(json, "name.last").unwrap().unwrap(); 133 | ``` 134 | 135 | ## Value 136 | 137 | Value types. 138 | ```rust 139 | enum Value { 140 | String(String), 141 | Number(Number), 142 | Object(String), 143 | Array(String), 144 | Boolean(bool), 145 | Null, 146 | } 147 | ``` 148 | 149 | Value has a number of methods that meet your different needs. 150 | 151 | ```rust 152 | value.get(&str) -> Option 153 | value.as_str() -> &str 154 | value.as_u64() -> u64 155 | value.as_i64() -> i64 156 | value.as_f64() -> f64 157 | value.as_bool() -> bool 158 | value.as_vec() -> Vec 159 | value.as_object() -> HashMap 160 | ``` 161 | 162 | 163 | ```rust 164 | value.is_number() -> bool 165 | value.is_string() -> bool 166 | value.is_bool() -> bool 167 | value.is_object() -> bool 168 | value.is_array() -> bool 169 | value.is_null() -> bool 170 | ``` 171 | 172 | ## Performance 173 | 174 | `$ cargo bench` 175 | 176 | * [ajson](https://github.com/importcjj/ajson) 177 | * [serde_json](https://github.com/serde-rs/json) 178 | * [rust-json](https://github.com/maciejhirsz/json-rust) 179 | 180 | ``` 181 | ajson benchmark time: [2.0816 us 2.0865 us 2.0917 us] 182 | change: [+0.6172% +0.9272% +1.2430%] (p = 0.00 < 0.05) 183 | Change within noise threshold. 184 | Found 11 outliers among 100 measurements (11.00%) 185 | 7 (7.00%) high mild 186 | 4 (4.00%) high severe 187 | 188 | serde_json benchmark time: [23.033 us 23.076 us 23.119 us] 189 | change: [-0.7185% -0.3455% +0.0230%] (p = 0.07 > 0.05) 190 | No change in performance detected. 191 | Found 7 outliers among 100 measurements (7.00%) 192 | 6 (6.00%) high mild 193 | 1 (1.00%) high severe 194 | 195 | json-rust benchmark time: [12.225 us 12.289 us 12.381 us] 196 | change: [-2.6200% -1.1789% +0.8442%] (p = 0.19 > 0.05) 197 | No change in performance detected. 198 | Found 9 outliers among 100 measurements (9.00%) 199 | 5 (5.00%) high mild 200 | 4 (4.00%) high severe 201 | 202 | ajson selector time: [1.1523 us 1.1561 us 1.1604 us] 203 | change: [+0.1567% +0.7278% +1.2945%] (p = 0.01 < 0.05) 204 | Change within noise threshold. 205 | Found 3 outliers among 100 measurements (3.00%) 206 | 3 (3.00%) high mild 207 | 208 | ajson multi query time: [559.19 ns 559.96 ns 560.77 ns] 209 | change: [-1.4268% -1.0380% -0.6698%] (p = 0.00 < 0.05) 210 | Change within noise threshold. 211 | Found 3 outliers among 100 measurements (3.00%) 212 | 3 (3.00%) high mild 213 | 214 | serde derive time: [4.5301 us 4.5403 us 4.5507 us] 215 | change: [-2.3423% -1.9438% -1.5697%] (p = 0.00 < 0.05) 216 | Performance has improved. 217 | Found 2 outliers among 100 measurements (2.00%) 218 | 2 (2.00%) high mild 219 | 220 | serde derive multi query 221 | time: [956.86 ns 962.64 ns 970.05 ns] 222 | change: [-1.7069% -1.0299% -0.2924%] (p = 0.01 < 0.05) 223 | Change within noise threshold. 224 | Found 9 outliers among 100 measurements (9.00%) 225 | 3 (3.00%) high mild 226 | 6 (6.00%) high severe 227 | 228 | nom json bench time: [2.9468 us 2.9515 us 2.9566 us] 229 | Found 5 outliers among 100 measurements (5.00%) 230 | 4 (4.00%) high mild 231 | 1 (1.00%) high severe 232 | ``` 233 | 234 | * MacBook Pro (14-inch, 2021) 235 | * Apple M1 Pro 236 | * 16 GB 237 | 238 | ## License 239 | MIT License. 240 | -------------------------------------------------------------------------------- /benches/ajson_benchmark.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use criterion::{black_box, Criterion}; 5 | extern crate ajson; 6 | extern crate json; 7 | 8 | #[macro_use] 9 | extern crate serde; 10 | extern crate nom; 11 | extern crate serde_json; 12 | mod nom_json; 13 | use json::JsonValue; 14 | use nom::error::ErrorKind; 15 | use serde_json::Value; 16 | 17 | #[allow(dead_code)] 18 | static BENCH_DATA: &str = r#"{ 19 | "overflow": 9223372036854775808, 20 | "widget": { 21 | "debug": "on", 22 | "window": { 23 | "title": "Sample Konfabulator Widget", 24 | "name": "main_window", 25 | "width": 500, 26 | "height": 500 27 | }, 28 | "image": { 29 | "src": "Images/Sun.png", 30 | "hOffset": 250, 31 | "vOffset": 250, 32 | "alignment": "center" 33 | }, 34 | "text": { 35 | "data": "Click Here", 36 | "size": 36, 37 | "style": "bold", 38 | "vOffset": 100, 39 | "alignment": "center", 40 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 41 | }, 42 | "menu": [ 43 | { 44 | "title": "file", 45 | "sub_item": 7, 46 | "options": [1, 2, 3] 47 | }, 48 | { 49 | "title": "edit", 50 | "sub_item": 14, 51 | "options": [4, 5] 52 | }, 53 | { 54 | "title": "help", 55 | "sub_item": 4, 56 | "options": [6, 7, 8] 57 | } 58 | ] 59 | } 60 | }"#; 61 | 62 | fn ajson_selector(json: &str) { 63 | black_box( 64 | ajson::get(json, "widget.[image.src,text.data]") 65 | .unwrap() 66 | .unwrap() 67 | .as_vec(), 68 | ); 69 | } 70 | 71 | fn ajson_multi_query(json: &str) { 72 | black_box([ 73 | ajson::get(json, "widget.image.src"), 74 | ajson::get(json, "widget.text.data"), 75 | ]); 76 | } 77 | 78 | fn ajson_path_bench() { 79 | black_box(ajson::Path::from_slice("widget.window.name".as_bytes())); 80 | black_box(ajson::Path::from_slice("widget.image.hOffset".as_bytes())); 81 | black_box(ajson::Path::from_slice("widget.text.onMouseUp".as_bytes())); 82 | black_box(ajson::Path::from_slice("widget.debug".as_bytes())); 83 | black_box(ajson::Path::from_slice( 84 | "widget.menu.#(sub_item>7)#.title".as_bytes(), 85 | )); 86 | } 87 | 88 | fn ajson_bench(json: &str) { 89 | black_box( 90 | ajson::get(json, "widget.window.name") 91 | .unwrap() 92 | .unwrap() 93 | .as_str(), 94 | ); 95 | black_box( 96 | ajson::get(json, "widget.image.hOffset") 97 | .unwrap() 98 | .unwrap() 99 | .as_f64(), 100 | ); 101 | black_box( 102 | ajson::get(json, "widget.text.onMouseUp") 103 | .unwrap() 104 | .unwrap() 105 | .as_str(), 106 | ); 107 | black_box(ajson::get(json, "widget.debug").unwrap().unwrap().as_str()); 108 | // black_box(ajson::get(json, "widget.text").unwrap().unwrap().as_object()); 109 | 110 | black_box( 111 | ajson::get(json, "widget.menu.#(sub_item>7)#.title") 112 | .unwrap() 113 | .unwrap() 114 | .as_vec(), 115 | ); 116 | // ajson::get(json, "widget.menu.[1.title,2.options]").as_array(); 117 | } 118 | 119 | fn gjson_selector(json: &str) { 120 | black_box(gjson::get(json, "widget.[image.src,text.data]").array()); 121 | } 122 | 123 | fn gjson_multi_query(json: &str) { 124 | black_box([ 125 | gjson::get(json, "widget.image.src"), 126 | gjson::get(json, "widget.text.data"), 127 | ]); 128 | } 129 | 130 | fn gjson_bench(json: &str) { 131 | black_box(gjson::get(json, "widget.window.name").str()); 132 | black_box(gjson::get(json, "widget.image.hOffset").f64()); 133 | black_box(gjson::get(json, "widget.text.onMouseUp").str()); 134 | black_box(gjson::get(json, "widget.debug").str()); 135 | 136 | black_box(gjson::get(json, "widget.menu.#(sub_item>7)#.title").array()); 137 | // gjson::get(json, "widget.menu.[1.title,2.options]").as_array(); 138 | } 139 | 140 | fn json_rust_bench(data: &str) { 141 | let a = &json::parse(data).unwrap(); 142 | black_box(a["widget"]["window"]["name"].as_str().unwrap()); 143 | let b = &json::parse(data).unwrap(); 144 | black_box(b["widget"]["image"]["hOffset"].as_i64().unwrap()); 145 | let c = &json::parse(data).unwrap(); 146 | black_box(c["widget"]["text"]["onMouseUp"].as_str().unwrap()); 147 | let d = &json::parse(data).unwrap(); 148 | black_box(d["widget"]["debug"].as_str().unwrap()); 149 | 150 | // let text = &serde_json::from_str::(BENCH_DATA).unwrap()["widget"]["text"] ; 151 | 152 | let menu = &json::parse(data).unwrap()["widget"]["menu"]; 153 | let _v: Vec<&JsonValue> = black_box( 154 | menu.members() 155 | .filter(|x| x["sub_item"].as_i64().unwrap() > 5) 156 | .map(|x| &x["title"]) 157 | .collect(), 158 | ); 159 | } 160 | 161 | fn serde_json_bench(json: &str) { 162 | let a = &serde_json::from_str::(json).unwrap(); 163 | black_box(a["widget"]["window"]["name"].as_str().unwrap()); 164 | let b = &serde_json::from_str::(json).unwrap(); 165 | black_box(b["widget"]["image"]["hOffset"].as_i64().unwrap()); 166 | let c = &serde_json::from_str::(json).unwrap(); 167 | black_box(c["widget"]["text"]["onMouseUp"].as_str().unwrap()); 168 | let d = &serde_json::from_str::(json).unwrap(); 169 | black_box(d["widget"]["debug"].as_str().unwrap()); 170 | 171 | // // let text = &serde_json::from_str::(BENCH_DATA).unwrap()["widget"]["text"] ; 172 | 173 | let menu = &serde_json::from_str::(json).unwrap(); 174 | 175 | let _v: Vec<&Value> = black_box( 176 | menu["widget"]["menu"] 177 | .as_array() 178 | .unwrap() 179 | .iter() 180 | .filter(|x| x["sub_item"].as_i64().unwrap() > 5) 181 | .map(|x| &x["title"]) 182 | .collect(), 183 | ); 184 | } 185 | 186 | fn serde_json_derive_bench(json: &str) { 187 | #![allow(non_snake_case)] 188 | 189 | { 190 | #[derive(Deserialize)] 191 | struct Main { 192 | widget: Widget, 193 | } 194 | #[derive(Deserialize)] 195 | struct Widget { 196 | window: Window, 197 | } 198 | #[derive(Deserialize)] 199 | struct Window { 200 | name: String, 201 | } 202 | 203 | let a = serde_json::from_str::
(json).unwrap(); 204 | black_box(a.widget.window.name); 205 | } 206 | 207 | { 208 | #[derive(Deserialize)] 209 | struct Main { 210 | widget: Widget, 211 | } 212 | #[derive(Deserialize)] 213 | struct Widget { 214 | image: Image, 215 | } 216 | #[derive(Deserialize)] 217 | struct Image { 218 | hOffset: i64, 219 | } 220 | let b = serde_json::from_str::
(json).unwrap(); 221 | black_box(b.widget.image.hOffset); 222 | } 223 | 224 | { 225 | #[derive(Deserialize)] 226 | struct Main { 227 | widget: Widget, 228 | } 229 | #[derive(Deserialize)] 230 | struct Widget { 231 | text: Text, 232 | } 233 | #[derive(Deserialize)] 234 | struct Text { 235 | onMouseUp: String, 236 | } 237 | let c = serde_json::from_str::
(json).unwrap(); 238 | black_box(c.widget.text.onMouseUp); 239 | } 240 | 241 | { 242 | #[derive(Deserialize)] 243 | struct Main { 244 | widget: Widget, 245 | } 246 | #[derive(Deserialize)] 247 | struct Widget { 248 | debug: String, 249 | } 250 | let d = serde_json::from_str::
(json).unwrap(); 251 | black_box(d.widget.debug); 252 | } 253 | 254 | { 255 | #[derive(Deserialize)] 256 | struct Main { 257 | widget: Widget, 258 | } 259 | #[derive(Deserialize)] 260 | struct Widget { 261 | menu: Vec, 262 | } 263 | #[derive(Deserialize)] 264 | struct Item { 265 | sub_item: i64, 266 | title: Value, 267 | } 268 | let e = serde_json::from_str::
(json).unwrap(); 269 | black_box( 270 | e.widget 271 | .menu 272 | .into_iter() 273 | .filter(|x| x.sub_item > 5) 274 | .map(|x| x.title) 275 | .collect::>(), 276 | ); 277 | } 278 | } 279 | 280 | fn nom_json_bench(json: &str) { 281 | black_box({ 282 | nom_json::root::<(&str, ErrorKind)>(json).map(|(_, value)| { 283 | value["widget"]["window"]["name"].as_str(); 284 | }); 285 | }); 286 | 287 | black_box({ 288 | nom_json::root::<(&str, ErrorKind)>(json).map(|(_, value)| { 289 | &value["widget"]["image"]["hOffset"]; 290 | }); 291 | }); 292 | 293 | black_box({ 294 | nom_json::root::<(&str, ErrorKind)>(json).map(|(_, value)| { 295 | value["widget"]["text"]["onMouseUp"].as_str(); 296 | }); 297 | }); 298 | 299 | black_box({ 300 | nom_json::root::<(&str, ErrorKind)>(json).map(|(_, value)| { 301 | value["widget"]["debug"].as_str(); 302 | }); 303 | }); 304 | 305 | black_box({ 306 | nom_json::root::<(&str, ErrorKind)>(json).map(|(_, value)| { 307 | let menu = &value["widget"]["menu"]; 308 | let v: Vec<&nom_json::JsonValue> = black_box( 309 | menu.members() 310 | .filter(|x| x["sub_item"].to_f64() > 5.0) 311 | .map(|x| &x["title"]) 312 | .collect(), 313 | ); 314 | 1 315 | }) 316 | }); 317 | } 318 | 319 | fn serde_json_derive_multi_query(json: &str) { 320 | #[derive(Deserialize)] 321 | struct Main { 322 | widget: Widget, 323 | } 324 | #[derive(Deserialize)] 325 | struct Widget { 326 | image: Image, 327 | text: Text, 328 | } 329 | #[derive(Deserialize)] 330 | struct Image { 331 | src: Value, 332 | } 333 | #[derive(Deserialize)] 334 | struct Text { 335 | data: Value, 336 | } 337 | 338 | let a = serde_json::from_str::
(json).unwrap(); 339 | black_box([a.widget.image.src, a.widget.text.data]); 340 | } 341 | 342 | fn criterion_benchmark(c: &mut Criterion) { 343 | // c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20)))); 344 | 345 | // c.bench_function("ajson path benchmark", |b| b.iter(ajson_path_bench)); 346 | 347 | c.bench_function("ajson benchmark", |b| { 348 | b.iter(|| ajson_bench(black_box(BENCH_DATA))) 349 | }); 350 | 351 | // c.bench_function("gjson benchmark", |b| { 352 | // b.iter(|| gjson_bench(black_box(BENCH_DATA))) 353 | // }); 354 | 355 | c.bench_function("serde_json benchmark", |b| { 356 | b.iter(|| serde_json_bench(black_box(BENCH_DATA))) 357 | }); 358 | 359 | c.bench_function("json-rust benchmark", |b| { 360 | b.iter(|| json_rust_bench(black_box(BENCH_DATA))) 361 | }); 362 | 363 | c.bench_function("ajson selector", |b| { 364 | b.iter(|| ajson_selector(black_box(BENCH_DATA))) 365 | }); 366 | 367 | // c.bench_function("gjson selector", |b| { 368 | // b.iter(|| gjson_selector(black_box(BENCH_DATA))) 369 | // }); 370 | 371 | c.bench_function("ajson multi query", |b| { 372 | b.iter(|| ajson_multi_query(black_box(BENCH_DATA))) 373 | }); 374 | 375 | // c.bench_function("gjson multi query", |b| { 376 | // b.iter(|| gjson_multi_query(black_box(BENCH_DATA))) 377 | // }); 378 | 379 | c.bench_function("serde derive", |b| { 380 | b.iter(|| serde_json_derive_bench(black_box(BENCH_DATA))) 381 | }); 382 | c.bench_function("serde derive multi query", |b| { 383 | b.iter(|| serde_json_derive_multi_query(black_box(BENCH_DATA))) 384 | }); 385 | 386 | c.bench_function("nom json bench", |b| { 387 | b.iter(|| nom_json_bench(black_box(BENCH_DATA))) 388 | }); 389 | } 390 | 391 | criterion_group!(benches, criterion_benchmark); 392 | criterion_main!(benches); 393 | -------------------------------------------------------------------------------- /benches/nom_json.rs: -------------------------------------------------------------------------------- 1 | extern crate nom; 2 | use std::{collections::HashMap, ops::Index, str}; 3 | 4 | // extern crate jemallocator; 5 | 6 | // #[global_allocator] 7 | // static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 8 | use nom::{ 9 | branch::alt, 10 | bytes::complete::{escaped, tag, take_while}, 11 | character::complete::{alphanumeric1 as alphanumeric, char, one_of}, 12 | combinator::{cut, map, opt}, 13 | error::{context, convert_error, ErrorKind, ParseError, VerboseError}, 14 | multi::separated_list, 15 | number::complete::double, 16 | sequence::{delimited, preceded, separated_pair, terminated}, 17 | Err, IResult, 18 | }; 19 | 20 | #[derive(Debug, PartialEq)] 21 | pub enum JsonValue { 22 | NULL, 23 | Str(String), 24 | Boolean(bool), 25 | Num(f64), 26 | Array(Vec), 27 | Object(HashMap), 28 | } 29 | 30 | pub type Members<'a> = ::std::slice::Iter<'a, JsonValue>; 31 | 32 | static NULL: JsonValue = JsonValue::NULL; 33 | 34 | impl JsonValue { 35 | pub fn as_str(&self) -> &str { 36 | match *self { 37 | JsonValue::Str(ref s) => s, 38 | _ => "", 39 | } 40 | } 41 | 42 | pub fn to_f64(&self) -> f64 { 43 | match *self { 44 | JsonValue::Num(f) => f, 45 | _ => 0.0, 46 | } 47 | } 48 | 49 | pub fn members(&self) -> Members { 50 | match *self { 51 | JsonValue::Array(ref vec) => vec.iter(), 52 | _ => [].iter(), 53 | } 54 | } 55 | } 56 | 57 | impl<'a> Index<&'a str> for JsonValue { 58 | type Output = JsonValue; 59 | 60 | fn index(&self, index: &str) -> &JsonValue { 61 | match *self { 62 | JsonValue::Object(ref object) => &object[index], 63 | _ => &NULL, 64 | } 65 | } 66 | } 67 | 68 | /// parser combinators are constructed from the bottom up: 69 | /// first we write parsers for the smallest elements (here a space character), 70 | /// then we'll combine them in larger parsers 71 | fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { 72 | let chars = " \t\r\n"; 73 | 74 | // nom combinators like `take_while` return a function. That function is the 75 | // parser,to which we can pass the input 76 | take_while(move |c| chars.contains(c))(i) 77 | } 78 | 79 | /// A nom parser has the following signature: 80 | /// `Input -> IResult`, with `IResult` defined as: 81 | /// `type IResult = Result<(I, O), Err>;` 82 | /// 83 | /// most of the times you can ignore the error type and use the default (but this 84 | /// examples shows custom error types later on!) 85 | /// 86 | /// Here we use `&str` as input type, but nom parsers can be generic over 87 | /// the input type, and work directly with `&[u8]` or any other type that 88 | /// implements the required traits. 89 | /// 90 | /// Finally, we can see here that the input and output type are both `&str` 91 | /// with the same lifetime tag. This means that the produced value is a subslice 92 | /// of the input data. and there is no allocation needed. This is the main idea 93 | /// behind nom's performance. 94 | fn parse_str<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { 95 | escaped(alphanumeric, '\\', one_of("\"n\\"))(i) 96 | } 97 | 98 | /// `tag(string)` generates a parser that recognizes the argument string. 99 | /// 100 | /// we can combine it with other functions, like `map` that takes the result 101 | /// of another parser, and applies a function over it (`map` itself generates 102 | /// a new parser`. 103 | /// 104 | /// `alt` is another combinator that tries multiple parsers one by one, until 105 | /// one of them succeeds 106 | fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> { 107 | alt((map(tag("false"), |_| false), map(tag("true"), |_| true)))(input) 108 | } 109 | 110 | /// this parser combines the previous `parse_str` parser, that recognizes the 111 | /// interior of a string, with a parse to recognize the double quote character, 112 | /// before the string (using `preceded`) and after the string (using `terminated`). 113 | /// 114 | /// `context` and `cut` are related to error management: 115 | /// - `cut` transforms an `Err::Error(e)` in `Err::Failure(e)`, signaling to 116 | /// combinators like `alt` that they should not try other parsers. We were in the 117 | /// right branch (since we found the `"` character) but encountered an error when 118 | /// parsing the string 119 | /// - `context` lets you add a static string to provide more information in the 120 | /// error chain (to indicate which parser had an error) 121 | fn string<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { 122 | context( 123 | "string", 124 | preceded(char('\"'), cut(terminated(parse_str, char('\"')))), 125 | )(i) 126 | } 127 | 128 | /// some combinators, like `separated_list` or `many0`, will call a parser repeatedly, 129 | /// accumulating results in a `Vec`, until it encounters an error. 130 | /// If you want more control on the parser application, check out the `iterator` 131 | /// combinator (cf `examples/iterator.rs`) 132 | fn array<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Vec, E> { 133 | context( 134 | "array", 135 | preceded( 136 | char('['), 137 | cut(terminated( 138 | separated_list(preceded(sp, char(',')), value), 139 | preceded(sp, char(']')), 140 | )), 141 | ), 142 | )(i) 143 | } 144 | 145 | fn key_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (&'a str, JsonValue), E> { 146 | separated_pair(preceded(sp, string), cut(preceded(sp, char(':'))), value)(i) 147 | } 148 | 149 | fn hash<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, HashMap, E> { 150 | context( 151 | "map", 152 | preceded( 153 | char('{'), 154 | cut(terminated( 155 | map( 156 | separated_list(preceded(sp, char(',')), key_value), 157 | |tuple_vec| { 158 | tuple_vec 159 | .into_iter() 160 | .map(|(k, v)| (String::from(k), v)) 161 | .collect() 162 | }, 163 | ), 164 | preceded(sp, char('}')), 165 | )), 166 | ), 167 | )(i) 168 | } 169 | 170 | /// here, we apply the space parser before trying to parse a value 171 | fn value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> { 172 | preceded( 173 | sp, 174 | alt(( 175 | map(hash, JsonValue::Object), 176 | map(array, JsonValue::Array), 177 | map(string, |s| JsonValue::Str(String::from(s))), 178 | map(double, JsonValue::Num), 179 | map(boolean, JsonValue::Boolean), 180 | )), 181 | )(i) 182 | } 183 | 184 | /// the root element of a JSON parser is either an object or an array 185 | pub fn root<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, JsonValue, E> { 186 | delimited( 187 | sp, 188 | alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))), 189 | opt(sp), 190 | )(i) 191 | } 192 | -------------------------------------------------------------------------------- /benches/traversing.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 4 | 5 | const BENCH_DATA: &str = r#"{ 6 | "overflow": 9223372036854775808, 7 | "widget": { 8 | "debug": "on", 9 | "window": { 10 | "title": "Sample Konfabulator Widget", 11 | "name": "main_window", 12 | "width": 500, 13 | "height": 500 14 | }, 15 | "image": { 16 | "src": "Images/Sun.png", 17 | "hOffset": 250, 18 | "vOffset": 250, 19 | "alignment": "center" 20 | }, 21 | "text": { 22 | "data": "Click Here", 23 | "size": 36, 24 | "style": "bold", 25 | "vOffset": 100, 26 | "alignment": "center", 27 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 28 | }, 29 | "menu": [ 30 | { 31 | "title": "file", 32 | "sub_item": 7, 33 | "options": [1, 2, 3] 34 | }, 35 | { 36 | "title": "edit", 37 | "sub_item": 14, 38 | "options": [4, 5] 39 | }, 40 | { 41 | "title": "help", 42 | "sub_item": 4, 43 | "options": [6, 7, 8] 44 | } 45 | ] 46 | } 47 | }"#; 48 | 49 | pub fn string(bytes: &[u8]) -> ajson::Result<(&[u8], &[u8])> { 50 | // skip check the first byte 51 | 52 | let mut i = 1; 53 | while i < bytes.len() { 54 | let b = unsafe { *bytes.get_unchecked(i) }; 55 | 56 | match b { 57 | b'"' => { 58 | i += 1; 59 | break; 60 | } 61 | b'\\' => { 62 | i += 1; 63 | } 64 | _ => {} 65 | } 66 | 67 | i += 1; 68 | } 69 | 70 | Ok(split_at(bytes, i)) 71 | } 72 | 73 | #[inline(always)] 74 | pub fn split_at(s: &[u8], mid: usize) -> (&[u8], &[u8]) { 75 | unsafe { (s.get_unchecked(..mid), s.get_unchecked(mid..s.len())) } 76 | } 77 | 78 | pub fn string_chunk(bytes: &[u8]) -> ajson::Result<(&[u8], &[u8])> { 79 | // skip check the first byte 80 | 81 | let mut i = 1; 82 | const CHUNK: usize = 4; 83 | 84 | 'outer: while i + CHUNK < bytes.len() { 85 | for _ in 0..CHUNK { 86 | let &b = unsafe { bytes.get_unchecked(i) }; 87 | i += 1; 88 | match b { 89 | b'"' => return Ok(split_at(bytes, i)), 90 | b'\\' => { 91 | i += 1; 92 | continue 'outer; 93 | } 94 | _ => {} 95 | } 96 | } 97 | } 98 | 99 | while i < bytes.len() { 100 | let b = unsafe { *bytes.get_unchecked(i) }; 101 | 102 | match b { 103 | b'"' => { 104 | i += 1; 105 | break; 106 | } 107 | b'\\' => { 108 | i += 1; 109 | } 110 | _ => {} 111 | } 112 | 113 | i += 1; 114 | } 115 | 116 | Ok(split_at(bytes, i)) 117 | } 118 | 119 | fn traversing(bytes: &[u8]) -> ajson::Result<(&[u8], &[u8])> { 120 | let mut i = 0; 121 | let mut depth = 1; 122 | 123 | while i < bytes.len() { 124 | let &b = unsafe { bytes.get_unchecked(i) }; 125 | match b { 126 | b'\\' => { 127 | i += 1; 128 | } 129 | b'"' => { 130 | let input = unsafe { bytes.get_unchecked(i..) }; 131 | let (s, _) = string(input).unwrap(); 132 | i += s.len(); 133 | continue; 134 | } 135 | b'[' | b'{' => depth += 1, 136 | b']' | b'}' => { 137 | depth -= 1; 138 | if depth == 0 { 139 | i += 1; 140 | break; 141 | } 142 | } 143 | _ => (), 144 | } 145 | i += 1; 146 | } 147 | 148 | return Ok(split_at(bytes, i)); 149 | 150 | // println!("{}", &json[..i]); 151 | } 152 | 153 | fn chunk_traversing(bytes: &[u8]) -> ajson::Result<(&[u8], &[u8])> { 154 | let mut i = 1; 155 | let mut depth = 1; 156 | 157 | const CHUNK_SIZE: usize = 32; 158 | 159 | 'outer: while i + CHUNK_SIZE < bytes.len() { 160 | for _ in 0..CHUNK_SIZE { 161 | let &b = unsafe { bytes.get_unchecked(i) }; 162 | 163 | match b { 164 | b'\\' => { 165 | i += 2; 166 | continue 'outer; 167 | } 168 | b'"' => { 169 | let input = unsafe { bytes.get_unchecked(i..) }; 170 | let (s, _) = string(input).unwrap(); 171 | 172 | i += s.len(); 173 | continue 'outer; 174 | } 175 | b'[' | b'{' => depth += 1, 176 | b']' | b'}' => { 177 | depth -= 1; 178 | if depth == 0 { 179 | i += 1; 180 | return Ok(split_at(bytes, i)); 181 | } 182 | } 183 | _ => (), 184 | } 185 | i += 1; 186 | } 187 | } 188 | 189 | while i < bytes.len() { 190 | let &b = unsafe { bytes.get_unchecked(i) }; 191 | match b { 192 | b'\\' => { 193 | i += 1; 194 | } 195 | b'"' => { 196 | let input = unsafe { bytes.get_unchecked(i..) }; 197 | let (s, _) = string(input).unwrap(); 198 | i += s.len(); 199 | continue; 200 | } 201 | b'[' | b'{' => depth += 1, 202 | b']' | b'}' => { 203 | depth -= 1; 204 | if depth == 0 { 205 | i += 1; 206 | break; 207 | } 208 | } 209 | _ => (), 210 | } 211 | i += 1; 212 | } 213 | 214 | return Ok(split_at(bytes, i)); 215 | } 216 | 217 | pub fn traverse_benchmark(c: &mut Criterion) { 218 | c.bench_function("traversing", |b| { 219 | b.iter(|| traversing(black_box(BENCH_DATA.as_bytes()))) 220 | }); 221 | 222 | c.bench_function("chunk traversing u8", |b| { 223 | b.iter(|| ajson::compound_u8(black_box(BENCH_DATA.as_bytes()))) 224 | }); 225 | } 226 | 227 | criterion_group!(benches, traverse_benchmark); 228 | criterion_main!(benches); 229 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | disallowed-types = [] -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | imports_granularity = "Crate" 3 | format_code_in_doc_comments = true 4 | reorder_impl_items = true 5 | group_imports = "StdExternalCrate" 6 | struct_field_align_threshold = 20 -------------------------------------------------------------------------------- /src/element.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, collections::HashMap}; 2 | 3 | use crate::{unescape, value::Value, Number, Result}; 4 | 5 | #[derive(PartialEq, Debug, Clone)] 6 | pub enum Element<'a> { 7 | String(&'a [u8], bool), 8 | Object(&'a [u8]), 9 | Array(&'a [u8]), 10 | Null(&'a [u8]), 11 | Boolean(&'a [u8]), 12 | Number(&'a [u8]), 13 | Count(usize), 14 | List(Vec>), 15 | Map(HashMap<(&'a [u8], bool), Element<'a>>), 16 | } 17 | 18 | impl<'a> Element<'a> { 19 | pub fn to_value(&self) -> Value<'a> { 20 | match &self { 21 | Element::String(buf, esc) => { 22 | if *esc { 23 | let s = unescape(&buf[1..buf.len() - 1]); 24 | Value::String(Cow::Owned(s)) 25 | } else { 26 | let s = unsafe { std::str::from_utf8_unchecked(&buf[1..buf.len() - 1]) }; 27 | Value::String(Cow::Borrowed(s)) 28 | } 29 | } 30 | Element::Object(s) => { 31 | let s = unsafe { std::str::from_utf8_unchecked(s) }; 32 | Value::Object(Cow::Borrowed(s)) 33 | } 34 | Element::Array(s) => { 35 | let s = unsafe { std::str::from_utf8_unchecked(s) }; 36 | Value::Array(Cow::Borrowed(s)) 37 | } 38 | Element::Boolean(buf) => match buf[0] { 39 | b't' => Value::Boolean(true), 40 | _ => Value::Boolean(false), 41 | }, 42 | Element::Number(buf) => { 43 | let n = Number::from(*buf); 44 | Value::Number(n) 45 | } 46 | Element::Count(num) => Value::Usize(*num), 47 | Element::List(elements) => { 48 | let mut array_string = String::new(); 49 | array_string.push('['); 50 | let size = elements.len(); 51 | 52 | for (i, element) in elements.iter().enumerate() { 53 | element.write_to_string_buffer(&mut array_string); 54 | if i < size - 1 { 55 | array_string.push(','); 56 | } 57 | } 58 | 59 | array_string.push(']'); 60 | Value::Array(Cow::Owned(array_string)) 61 | } 62 | Element::Map(elements) => { 63 | let mut object_string = String::new(); 64 | object_string.push('{'); 65 | let size = elements.len(); 66 | let mut count = 0; 67 | 68 | for ((buf, esc), element) in elements { 69 | count += 1; 70 | 71 | if *esc { 72 | let s = unescape(&buf[1..buf.len() - 1]); 73 | object_string.push_str(s.as_str()); 74 | } else { 75 | let s = unsafe { std::str::from_utf8_unchecked(buf) }; 76 | object_string.push_str(s); 77 | }; 78 | 79 | object_string.push(':'); 80 | element.write_to_string_buffer(&mut object_string); 81 | if count < size { 82 | object_string.push(','); 83 | } 84 | } 85 | 86 | object_string.push('}'); 87 | Value::Object(Cow::Owned(object_string)) 88 | } 89 | Element::Null(_) => Value::Null, 90 | } 91 | } 92 | 93 | fn write_to_string_buffer(&self, buffer: &mut String) { 94 | match *self { 95 | Element::String(buf, esc) => { 96 | if esc { 97 | let s = unescape(&buf[1..buf.len() - 1]); 98 | buffer.push_str(s.as_str()); 99 | } else { 100 | let s = unsafe { std::str::from_utf8_unchecked(buf) }; 101 | buffer.push_str(s); 102 | }; 103 | } 104 | 105 | Element::Object(s) 106 | | Element::Array(s) 107 | | Element::Boolean(s) 108 | | Element::Number(s) 109 | | Element::Null(s) => { 110 | let s = unsafe { std::str::from_utf8_unchecked(s) }; 111 | buffer.push_str(s); 112 | } 113 | Element::List(ref elements) => { 114 | buffer.push('['); 115 | let size = elements.len(); 116 | 117 | for (i, element) in elements.iter().enumerate() { 118 | element.write_to_string_buffer(buffer); 119 | if i < size - 1 { 120 | buffer.push(','); 121 | } 122 | } 123 | 124 | buffer.push(']'); 125 | } 126 | _ => (), 127 | } 128 | } 129 | } 130 | 131 | pub fn true_u8(bytes: &[u8]) -> Result<(&[u8], &[u8])> { 132 | if bytes.len() < 4 { 133 | return Err(crate::Error::Eof); 134 | } 135 | 136 | Ok(split_at_u8(bytes, 4)) 137 | } 138 | 139 | pub fn false_u8(bytes: &[u8]) -> Result<(&[u8], &[u8])> { 140 | if bytes.len() < 5 { 141 | return Err(crate::Error::Eof); 142 | } 143 | 144 | Ok(split_at_u8(bytes, 5)) 145 | } 146 | 147 | pub fn null_u8(bytes: &[u8]) -> Result<(&[u8], &[u8])> { 148 | if bytes.len() < 4 { 149 | return Err(crate::Error::Eof); 150 | } 151 | 152 | Ok(split_at_u8(bytes, 4)) 153 | } 154 | 155 | #[inline(always)] 156 | pub fn split_at_u8(s: &[u8], mid: usize) -> (&[u8], &[u8]) { 157 | unsafe { (s.get_unchecked(..mid), s.get_unchecked(mid..s.len())) } 158 | } 159 | 160 | pub fn string_u8(bytes: &[u8]) -> Result<(&[u8], &[u8], bool)> { 161 | // skip check the first byte 162 | 163 | const TABLE: [u8; 256] = { 164 | let mut table: [u8; 256] = [0; 256]; 165 | table[b'"' as usize] = 1; 166 | table[b'\\' as usize] = 1; 167 | table 168 | }; 169 | 170 | let mut i = 1; 171 | let mut esc = false; 172 | while i < bytes.len() { 173 | let b = unsafe { *bytes.get_unchecked(i) }; 174 | if TABLE[b as usize] == 0 { 175 | i += 1; 176 | continue; 177 | } 178 | 179 | match b { 180 | b'"' => { 181 | i += 1; 182 | break; 183 | } 184 | b'\\' => { 185 | esc = true; 186 | i += 1; 187 | } 188 | _ => {} 189 | } 190 | 191 | i += 1; 192 | } 193 | 194 | let (a, b) = split_at_u8(bytes, i); 195 | 196 | Ok((a, b, esc)) 197 | } 198 | 199 | #[cfg(test)] 200 | mod test_strin_u8 { 201 | use super::string_u8; 202 | 203 | #[test] 204 | fn test_string() { 205 | assert_eq!( 206 | string_u8(r#""hello": "tom""#.as_bytes()), 207 | Ok((r#""hello""#.as_bytes(), r#": "tom""#.as_bytes(), false)) 208 | ); 209 | 210 | assert_eq!( 211 | string_u8(r#""hello"#.as_bytes()), 212 | Ok((r#""hello"#.as_bytes(), r#""#.as_bytes(), false)) 213 | ); 214 | } 215 | } 216 | 217 | pub fn compound_u8(bytes: &[u8]) -> Result<(&[u8], &[u8])> { 218 | fn skip_one(input: &[u8], _: usize) -> Result { 219 | Ok(1 + input.len()) 220 | } 221 | 222 | fn skip_string(input: &[u8], i: usize) -> Result { 223 | let input = unsafe { input.get_unchecked(i..) }; 224 | let (s, _, _) = string_u8(input)?; 225 | Ok(s.len()) 226 | } 227 | 228 | fn skip_compound(input: &[u8], i: usize) -> Result { 229 | let input = unsafe { input.get_unchecked(i..) }; 230 | let (s, _) = compound_u8(input)?; 231 | Ok(s.len()) 232 | } 233 | 234 | type Traverse = fn(&[u8], i: usize) -> Result; 235 | const TABLE: [Option; 256] = { 236 | let mut table: [Option; 256] = [None; 256]; 237 | table[b'"' as usize] = Some(skip_string); 238 | table[b'[' as usize] = Some(skip_compound); 239 | table[b'{' as usize] = Some(skip_compound); 240 | table[b']' as usize] = Some(skip_one); 241 | table[b'}' as usize] = Some(skip_one); 242 | table 243 | }; 244 | 245 | let mut i = 1; 246 | const CHUNK: usize = 8; 247 | 'outer: while i + CHUNK < bytes.len() { 248 | for _ in 0..CHUNK { 249 | let &b = unsafe { bytes.get_unchecked(i) }; 250 | match TABLE[b as usize] { 251 | Some(t) => { 252 | i += t(bytes, i)?; 253 | continue 'outer; 254 | } 255 | None => { 256 | i += 1; 257 | } 258 | } 259 | } 260 | } 261 | 262 | while i < bytes.len() { 263 | let &b = unsafe { bytes.get_unchecked(i) }; 264 | match TABLE[b as usize] { 265 | Some(t) => { 266 | i += t(bytes, i)?; 267 | } 268 | None => { 269 | i += 1; 270 | } 271 | } 272 | } 273 | 274 | if i > bytes.len() { 275 | i -= bytes.len(); 276 | } 277 | 278 | return Ok(split_at_u8(bytes, i)); 279 | } 280 | 281 | #[cfg(test)] 282 | mod test_compound_u8 { 283 | use super::{compound_u8, Result}; 284 | 285 | #[test] 286 | fn test_compound() -> Result<()> { 287 | const JSON: &str = r#"{"1":"2", "name": "jack"}xxxx"#; 288 | let r = compound_u8(JSON.as_bytes())?; 289 | 290 | assert_eq!(r.0, r#"{"1":"2", "name": "jack"}"#.as_bytes()); 291 | assert_eq!(r.1, "xxxx".as_bytes()); 292 | 293 | Ok(()) 294 | } 295 | } 296 | 297 | pub fn number_u8(bytes: &[u8]) -> Result<(&[u8], &[u8])> { 298 | let mut i = 0; 299 | 300 | while i < bytes.len() { 301 | let b = unsafe { *bytes.get_unchecked(i) }; 302 | 303 | match b { 304 | b'0'..=b'9' => (), 305 | b'-' | b'.' => (), 306 | _ => break, 307 | } 308 | i += 1; 309 | } 310 | 311 | Ok(split_at_u8(bytes, i)) 312 | } 313 | 314 | pub type MakeResult<'a> = Result<(Option>, &'a [u8])>; 315 | 316 | pub type MakeFn = fn(&[u8]) -> MakeResult; 317 | 318 | fn make_string(input: &[u8]) -> MakeResult { 319 | string_u8(input).map(|(a, b, esc)| (Some(Element::String(a, esc)), b)) 320 | } 321 | 322 | fn make_true(input: &[u8]) -> MakeResult { 323 | true_u8(input).map(|(a, b)| (Some(Element::Boolean(a)), b)) 324 | } 325 | 326 | fn make_false(input: &[u8]) -> MakeResult { 327 | false_u8(input).map(|(a, b)| (Some(Element::Boolean(a)), b)) 328 | } 329 | 330 | fn make_null(input: &[u8]) -> MakeResult { 331 | null_u8(input).map(|(a, b)| (Some(Element::Null(a)), b)) 332 | } 333 | 334 | fn make_number(input: &[u8]) -> MakeResult { 335 | number_u8(input).map(|(a, b)| (Some(Element::Number(a)), b)) 336 | } 337 | 338 | fn make_array(input: &[u8]) -> MakeResult { 339 | compound_u8(input).map(|(a, b)| (Some(Element::Array(a)), b)) 340 | } 341 | 342 | fn make_object(input: &[u8]) -> MakeResult { 343 | compound_u8(input).map(|(a, b)| (Some(Element::Object(a)), b)) 344 | } 345 | 346 | pub fn read_one(input: &[u8]) -> Result<(Option, &[u8])> { 347 | let mut i = 0; 348 | 349 | const MAKER: [Option; 256] = { 350 | let mut table: [Option; 256] = [None; 256]; 351 | table[b'"' as usize] = Some(make_string); 352 | table[b't' as usize] = Some(make_true); 353 | table[b'f' as usize] = Some(make_false); 354 | table[b'n' as usize] = Some(make_null); 355 | table[b'{' as usize] = Some(make_object); 356 | table[b'}' as usize] = Some(|input| Ok((None, input))); 357 | table[b'[' as usize] = Some(make_array); 358 | table[b']' as usize] = Some(|input| Ok((None, input))); 359 | table[b'0' as usize] = Some(make_number); 360 | table[b'1' as usize] = Some(make_number); 361 | table[b'2' as usize] = Some(make_number); 362 | table[b'3' as usize] = Some(make_number); 363 | table[b'4' as usize] = Some(make_number); 364 | table[b'5' as usize] = Some(make_number); 365 | table[b'6' as usize] = Some(make_number); 366 | table[b'7' as usize] = Some(make_number); 367 | table[b'8' as usize] = Some(make_number); 368 | table[b'9' as usize] = Some(make_number); 369 | table[b'-' as usize] = Some(make_number); 370 | table[b'.' as usize] = Some(make_number); 371 | 372 | table 373 | }; 374 | 375 | while i < input.len() { 376 | let b = unsafe { *input.get_unchecked(i) }; 377 | 378 | match MAKER[b as usize] { 379 | Some(make_fn) => { 380 | let input = unsafe { input.get_unchecked(i..) }; 381 | return make_fn(input); 382 | } 383 | None => { 384 | i += 1; 385 | } 386 | } 387 | } 388 | Ok((None, "".as_bytes())) 389 | } 390 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # A-JSON 3 | Ajson is a lightweight json parser that allows users to dynamically retrieve values in json using a simple syntax. 4 | 5 | ## Examples 6 | ``` 7 | use ajson::Result; 8 | fn main() -> Result<()> { 9 | let data = r#" 10 | { 11 | "project": { 12 | "name": "ajson", 13 | "maintainer": "importcjj", 14 | "version": 0.1, 15 | "rusts": ["stable", "nightly"] 16 | } 17 | } 18 | "#; 19 | 20 | let name = ajson::get(data, "project.name")?.unwrap(); 21 | assert_eq!(name, "ajson"); 22 | Ok(()) 23 | } 24 | ``` 25 | ## Syntax 26 | JSON example 27 | ```text 28 | { 29 | "name": {"first": "Tom", "last": "Anderson"}, 30 | "age":37, 31 | "children": ["Sara","Alex","Jack"], 32 | "fav.movie": "Deer Hunter", 33 | "friends": [ 34 | {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, 35 | {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, 36 | {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} 37 | ] 38 | } 39 | ``` 40 | 41 | #### basic 42 | Below is a quick overview of the path syntax, for more complete information please check out GJSON Syntax. 43 | 44 | A path is a series of keys separated by a dot. A key may contain special wildcard characters '*' and '?'. To access an array value use the index as the key. To get the number of elements in an array or to access a child path, use the '#' character. The dot and wildcard characters can be escaped with ''. 45 | 46 | ```text 47 | name.last >> "Anderson" 48 | age >> 37 49 | children >> ["Sara","Alex","Jack"] 50 | children.# >> 3 51 | children.1 >> "Alex" 52 | child*.2 >> "Jack" 53 | c?ildren.0 >> "Sara" 54 | fav\.movie >> "Deer Hunter" 55 | friends.#.first >> ["Dale","Roger","Jane"] 56 | friends.1.last >> "Craig" 57 | ``` 58 | 59 | #### Escape character 60 | Special purpose characters, such as ., *, and ? can be escaped with . 61 | 62 | ```text 63 | fav\.movie "Deer Hunter" 64 | ``` 65 | 66 | #### Arrays 67 | The # character allows for digging into JSON Arrays.To get the length of an array you'll just use the # all by itself. 68 | ```text 69 | friends.# 3 70 | friends.#.age [44,68,47] 71 | ``` 72 | 73 | #### queries 74 | You can also query an array for the first match by using #(...), or find all matches with #(...)#. Queries support the ==, !=, <, <=, >, >= comparison operators and the simple pattern matching % (like) and !% (not like) operators. 75 | 76 | ```text 77 | friends.#(last=="Murphy").first >> "Dale" 78 | friends.#(last=="Murphy")#.first >> ["Dale","Jane"] 79 | friends.#(age>45)#.last >> ["Craig","Murphy"] 80 | friends.#(first%"D*").last >> "Murphy" 81 | friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] 82 | ``` 83 | 84 | #### construct 85 | Basically, you can use selectors to assemble whatever you want, and of course, the result is still a json ;) 86 | ```text 87 | {name.first,age,"murphys":friends.#(last="Murphy")#.first} 88 | [name.first,age,children.0] 89 | ``` 90 | */ 91 | 92 | #[cfg(feature = "wild")] 93 | extern crate regex; 94 | #[cfg(feature = "wild")] 95 | mod wild; 96 | 97 | mod element; 98 | mod number; 99 | mod parser; 100 | mod path; 101 | mod unescape; 102 | mod util; 103 | mod value; 104 | 105 | use std::result; 106 | 107 | #[doc(hidden)] 108 | pub use element::compound_u8; 109 | pub use number::Number; 110 | pub use path::Path; 111 | pub use unescape::unescape; 112 | pub use value::Value; 113 | 114 | #[derive(Debug, PartialEq, Eq)] 115 | pub enum Error { 116 | Path, 117 | Eof, 118 | ObjectKey, 119 | Object, 120 | Array, 121 | } 122 | 123 | pub type Result = result::Result; 124 | 125 | /// `get` value from JSON string with the specified path, it is relatively loose and 126 | /// can tolerate some illegal JSON. 127 | /// ``` 128 | /// use ajson::Result; 129 | /// fn main() -> Result<()> { 130 | /// let data = r#"{"name": "ajson"}"#; 131 | /// let v = ajson::get(data, "name")?.unwrap(); 132 | /// assert_eq!(v, "ajson"); 133 | /// Ok(()) 134 | /// } 135 | /// ``` 136 | /// If the given JSON is not a valid JSON, then ajson 137 | /// will try to get the value from the first JSON array 138 | /// or map it finds. 139 | /// ``` 140 | /// use ajson::Result; 141 | /// fn main() -> Result<()> { 142 | /// let data = r#"someinvalidstring{"name": "ajson"}"#; 143 | /// let v = ajson::get(data, "name")?.unwrap(); 144 | /// assert_eq!(v, "ajson"); 145 | /// Ok(()) 146 | /// } 147 | /// ``` 148 | /// If there is no valid JSON array or map in the given JSON, 149 | /// `get` returns None. 150 | /// ```should_panic 151 | /// let data = r#"someinvalidstring"#; 152 | /// let v = ajson::get(data, "name").unwrap().unwrap(); 153 | /// ``` 154 | pub fn get<'a>(json: &'a str, path: &'a str) -> Result>> { 155 | let path = path::Path::from_slice(path.as_bytes())?; 156 | let (a, _left) = parser::bytes_get(json.as_bytes(), &path)?; 157 | Ok(a.map(|el| el.to_value())) 158 | } 159 | 160 | /// Returns the first JSON value parsed, and it may be having 161 | /// problems because it does not actively panic on incomplete 162 | /// JSON values. For example, array or map are not closed properly. 163 | /// ``` 164 | /// use ajson::Result; 165 | /// fn main() -> Result<()> { 166 | /// let v = ajson::parse(r#"{"name": "ajson"}"#)?.unwrap(); 167 | /// assert!(v.is_object()); 168 | /// let v = ajson::parse(r#"{"name": "ajson""#)?.unwrap(); 169 | /// assert!(v.is_object()); 170 | /// let v = ajson::parse(r#"null,"string", 2"#)?.unwrap(); 171 | /// assert!(v.is_null()); 172 | /// Ok(()) 173 | /// } 174 | /// ``` 175 | pub fn parse(json: &str) -> Result> { 176 | let (parsed, _left) = element::read_one(json.as_bytes())?; 177 | 178 | Ok(parsed.map(|el| el.to_value())) 179 | } 180 | -------------------------------------------------------------------------------- /src/number.rs: -------------------------------------------------------------------------------- 1 | use std::convert::From; 2 | const MIN_UINT_53: u64 = 0; 3 | const MAX_UINT_53: u64 = 4503599627370495; 4 | const MIN_INT_53: i64 = -2251799813685248; 5 | const MAX_INT_53: i64 = 2251799813685247; 6 | const ZERO_UINT: u64 = 0; 7 | const ZERO_INT: i64 = 0; 8 | const ZERO_FLOAT: f64 = 0.0; 9 | const ZERO_FLOAT_F32: f32 = 0.0; 10 | 11 | // const ZERO_INT_I32: i32 = 0; 12 | // const ZERO_INT_U32: u32 = 0; 13 | 14 | #[derive(Debug, PartialEq, Eq, Clone)] 15 | pub enum Number<'a> { 16 | F64(&'a str), 17 | U64(&'a str), 18 | I64(&'a str), 19 | } 20 | 21 | impl<'a> From<&'a [u8]> for Number<'a> { 22 | fn from(v: &[u8]) -> Number { 23 | let sign = match v.first() { 24 | Some(b'-') => true, 25 | None => panic!("invalid number"), 26 | _ => false, 27 | }; 28 | 29 | let mut float = false; 30 | let mut i = 1; 31 | while i < v.len() { 32 | let &b = unsafe { v.get_unchecked(i) }; 33 | match b { 34 | b'0'..=b'9' => (), 35 | b'.' => float = true, 36 | _ => { 37 | break; 38 | } 39 | }; 40 | i += 1; 41 | } 42 | 43 | let s = unsafe { std::str::from_utf8_unchecked(v.get_unchecked(0..i)) }; 44 | 45 | if float { 46 | Number::F64(s) 47 | } else if sign { 48 | Number::I64(s) 49 | } else { 50 | Number::U64(s) 51 | } 52 | } 53 | } 54 | 55 | impl<'a> Number<'a> { 56 | pub fn as_str(&self) -> &str { 57 | match self { 58 | Number::F64(s) => s, 59 | Number::U64(s) => s, 60 | Number::I64(s) => s, 61 | } 62 | } 63 | 64 | pub fn to_f64(&self) -> f64 { 65 | match self { 66 | Number::F64(s) => s.parse().unwrap_or(ZERO_FLOAT), 67 | Number::U64(s) => s.parse().unwrap_or(ZERO_FLOAT), 68 | Number::I64(s) => s.parse().unwrap_or(ZERO_FLOAT), 69 | } 70 | } 71 | 72 | pub fn to_f32(&self) -> f32 { 73 | match self { 74 | Number::F64(s) => s.parse().unwrap_or(ZERO_FLOAT_F32), 75 | Number::U64(s) => s.parse().unwrap_or(ZERO_FLOAT_F32), 76 | Number::I64(s) => s.parse().unwrap_or(ZERO_FLOAT_F32), 77 | } 78 | } 79 | 80 | pub fn to_u64(&self) -> u64 { 81 | match self { 82 | Number::F64(s) => { 83 | f64_to_u64(self.to_f64()).unwrap_or_else(|| parse_uint_lossy(s.as_bytes())) 84 | } 85 | Number::I64(s) => s.parse().unwrap_or(ZERO_UINT), 86 | Number::U64(s) => s.parse().unwrap_or(ZERO_UINT), 87 | } 88 | } 89 | 90 | pub fn to_i64(&self) -> i64 { 91 | match self { 92 | Number::F64(s) => { 93 | f64_to_i64(self.to_f64()).unwrap_or_else(|| parse_int_lossy(s.as_bytes())) 94 | } 95 | Number::I64(s) => s.parse().unwrap_or(ZERO_INT), 96 | Number::U64(s) => s.parse().unwrap_or(ZERO_INT), 97 | } 98 | } 99 | } 100 | 101 | fn f64_to_u64(f: f64) -> Option { 102 | let u = f as u64; 103 | match u { 104 | MIN_UINT_53..=MAX_UINT_53 => Some(u), 105 | _ => None, 106 | } 107 | } 108 | 109 | fn f64_to_i64(f: f64) -> Option { 110 | let i = f as i64; 111 | match i { 112 | MIN_INT_53..=MAX_INT_53 => Some(i), 113 | _ => None, 114 | } 115 | } 116 | 117 | pub fn parse_uint_lossy(v: &[u8]) -> u64 { 118 | let mut acc: u64 = 0; 119 | for b in v { 120 | match b { 121 | b'0'..=b'9' => acc = acc * 10 + (*b - 48) as u64, 122 | _ => return acc, 123 | } 124 | } 125 | 126 | acc 127 | } 128 | 129 | pub fn parse_int_lossy(v: &[u8]) -> i64 { 130 | if v.is_empty() { 131 | return ZERO_INT; 132 | } 133 | 134 | let sign = v[0] == b'-'; 135 | let mut acc: i64 = 0; 136 | 137 | for b in v { 138 | match b { 139 | b'0'..=b'9' => match sign { 140 | true => acc = acc * 10 - (*b - 48) as i64, 141 | false => acc = acc * 10 + (*b - 48) as i64, 142 | }, 143 | _ => return acc, 144 | } 145 | } 146 | 147 | acc 148 | } 149 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, str}; 2 | 3 | use crate::{ 4 | element, 5 | element::Element, 6 | path::{Path, SubSelector}, 7 | value::Value, 8 | Error, Result, 9 | }; 10 | 11 | pub fn bytes_to_vec(mut bytes: &[u8]) -> Result> { 12 | let mut arr = Vec::new(); 13 | let mut i = 0; 14 | while i < bytes.len() { 15 | let b = unsafe { *bytes.get_unchecked(i) }; 16 | if b == b'[' { 17 | break; 18 | }; 19 | i += 1; 20 | } 21 | 22 | if i >= bytes.len() - 1 { 23 | return Ok(arr); 24 | } 25 | 26 | bytes = &bytes[i + 1..]; 27 | 28 | loop { 29 | let (a, left) = element::read_one(bytes)?; 30 | bytes = left; 31 | match a { 32 | Some(element) => { 33 | arr.push(element.to_value()); 34 | continue; 35 | } 36 | None => break, 37 | } 38 | } 39 | 40 | Ok(arr) 41 | } 42 | 43 | pub fn bytes_to_map(mut bytes: &[u8]) -> Result> { 44 | let mut m = HashMap::new(); 45 | 46 | let mut i = 0; 47 | while i < bytes.len() { 48 | let b = unsafe { *bytes.get_unchecked(i) }; 49 | if b == b'{' { 50 | break; 51 | }; 52 | i += 1; 53 | } 54 | 55 | if i == bytes.len() - 1 { 56 | return Ok(m); 57 | } 58 | 59 | if i >= bytes.len() { 60 | return Err(Error::Object); 61 | } 62 | 63 | i += 1; 64 | 65 | while i < bytes.len() { 66 | let &b = unsafe { bytes.get_unchecked(i) }; 67 | if b == b'}' { 68 | break; 69 | } 70 | 71 | if b != b'"' { 72 | i += 1; 73 | continue; 74 | } 75 | 76 | let input = unsafe { bytes.get_unchecked(i..) }; 77 | let (key, b, _esc) = element::string_u8(input)?; 78 | bytes = b; 79 | 80 | let (val_element, b) = element::read_one(bytes)?; 81 | bytes = b; 82 | i = 0; 83 | match val_element { 84 | Some(element) => { 85 | let s = unsafe { std::str::from_utf8_unchecked(&key[1..key.len() - 1]) }; 86 | let value = element.to_value(); 87 | m.insert(s, value); 88 | } 89 | None => break, 90 | } 91 | } 92 | 93 | Ok(m) 94 | } 95 | 96 | pub fn bytes_get<'a>(bytes: &'a [u8], path: &Path<'a>) -> Result<(Option>, &'a [u8])> { 97 | if !path.ok || bytes.is_empty() { 98 | return Ok((None, "".as_bytes())); 99 | } 100 | 101 | if path.has_selectors() { 102 | let element = match path.arrsel { 103 | true => select_to_array(bytes, path.borrow_selectors())?, 104 | false => select_to_object(bytes, path.borrow_selectors())?, 105 | }; 106 | 107 | match element { 108 | Some(element) => { 109 | if path.more { 110 | let next = path.parse_next()?; 111 | let element = element_get(element, &next)?; 112 | return Ok((element, "".as_bytes())); 113 | } else { 114 | return Ok((Some(element), "".as_bytes())); 115 | } 116 | } 117 | None => return Ok((None, "".as_bytes())), 118 | } 119 | } 120 | 121 | let mut i = 0; 122 | 123 | type Getter = for<'a> fn(&'a [u8], &Path<'a>) -> element::MakeResult<'a>; 124 | 125 | const GETTER: [Option; 256] = { 126 | let mut table: [Option; 256] = [None; 256]; 127 | table[b'{' as usize] = Some(object_bytes_get); 128 | table[b'[' as usize] = Some(array_bytes_get); 129 | 130 | table 131 | }; 132 | 133 | while i < bytes.len() { 134 | let b = unsafe { *bytes.get_unchecked(i) }; 135 | match GETTER[b as usize] { 136 | None => (), 137 | Some(getter_fn) => { 138 | let input = unsafe { bytes.get_unchecked(i..) }; 139 | return getter_fn(input, path); 140 | } 141 | } 142 | 143 | i += 1; 144 | } 145 | 146 | Ok((None, "".as_bytes())) 147 | } 148 | 149 | fn select_to_object<'a>(input: &'a [u8], sels: &[SubSelector<'a>]) -> Result>> { 150 | let mut map = HashMap::new(); 151 | 152 | for sel in sels { 153 | let path = Path::from_slice(sel.path)?; 154 | if let (Some(sub_pv), _) = bytes_get(input, &path)? { 155 | map.insert((sel.name, false), sub_pv); 156 | } 157 | } 158 | 159 | Ok(Some(Element::Map(map))) 160 | } 161 | 162 | fn select_to_array<'a>(input: &'a [u8], sels: &[SubSelector<'a>]) -> Result>> { 163 | let mut list = Vec::new(); 164 | 165 | for sel in sels { 166 | let path = Path::from_slice(sel.path)?; 167 | if let (Some(sub_pv), _) = bytes_get(input, &path)? { 168 | list.push(sub_pv) 169 | } 170 | } 171 | 172 | Ok(Some(Element::List(list))) 173 | } 174 | 175 | fn element_ref_get<'a>(element: &Element<'a>, path: &Path<'a>) -> Result>> { 176 | if !path.ok { 177 | return Ok(None); 178 | } 179 | 180 | match element { 181 | Element::Array(s) | Element::Object(s) => { 182 | let (a, _b) = bytes_get(s, path)?; 183 | Ok(a) 184 | } 185 | _ => Ok(None), 186 | } 187 | } 188 | 189 | fn element_get<'a>(element: Element<'a>, path: &Path<'a>) -> Result>> { 190 | if !path.ok { 191 | return Ok(None); 192 | } 193 | 194 | let next_path = path.parse_next()?; 195 | match element { 196 | Element::Array(s) | Element::Object(s) => { 197 | let (a, _b) = bytes_get(s, path)?; 198 | Ok(a) 199 | } 200 | Element::Map(m) => { 201 | for (key, value) in m.into_iter() { 202 | if path.is_match(key.0, key.1) { 203 | if path.more { 204 | return element_get(value, &next_path); 205 | } 206 | return Ok(Some(value)); 207 | } 208 | } 209 | 210 | Ok(None) 211 | } 212 | Element::List(elements) => { 213 | let query = path.borrow_query(); 214 | let query_list = (query.on && query.all) || (!query.on && path.more); 215 | let query_first = query.on && !query.all; 216 | 217 | if query_first { 218 | if !elements.is_empty() { 219 | let first = elements.into_iter().next().unwrap(); 220 | if path.more { 221 | return element_get(first, &next_path); 222 | } 223 | return Ok(Some(first)); 224 | } 225 | 226 | return Ok(None); 227 | } 228 | 229 | if query_list { 230 | if path.more { 231 | let mut results = vec![]; 232 | for element in elements.into_iter() { 233 | if let Some(sub) = element_get(element, &next_path)? { 234 | results.push(sub); 235 | } 236 | } 237 | return Ok(Some(Element::List(results))); 238 | } 239 | return Ok(Some(Element::List(elements))); 240 | } 241 | 242 | Ok(Some(Element::Count(elements.len()))) 243 | } 244 | _ => Ok(None), 245 | } 246 | } 247 | 248 | #[inline] 249 | fn object_bytes_get<'a>( 250 | mut input: &'a [u8], 251 | path: &Path<'a>, 252 | ) -> Result<(Option>, &'a [u8])> { 253 | let mut i = 1; 254 | 255 | while i < input.len() { 256 | let &b = unsafe { input.get_unchecked(i) }; 257 | if b == b'}' { 258 | i += 1; 259 | return Ok((None, &input[i..])); 260 | } 261 | 262 | if b != b'"' { 263 | i += 1; 264 | continue; 265 | } 266 | 267 | input = unsafe { input.get_unchecked(i..) }; 268 | let (s, left, esc) = element::string_u8(input)?; 269 | input = left; 270 | 271 | // object key 272 | if path.is_match(&s[1..s.len() - 1], esc) { 273 | return if path.more { 274 | let next_path = path.parse_next()?; 275 | bytes_get(input, &next_path) 276 | } else { 277 | element::read_one(input) 278 | }; 279 | } 280 | 281 | let (element, left) = element::read_one(input)?; 282 | if element.is_none() { 283 | return Ok((None, "".as_bytes())); 284 | } 285 | input = left; 286 | i = 0; 287 | } 288 | 289 | Ok((None, "".as_bytes())) 290 | } 291 | 292 | fn array_bytes_get<'a>( 293 | mut bytes: &'a [u8], 294 | path: &Path<'a>, 295 | ) -> Result<(Option>, &'a [u8])> { 296 | let mut index = 0; 297 | let (idx, get_idx) = match str::from_utf8(path.part) 298 | .map_err(|_| Error::Path)? 299 | .parse::() 300 | { 301 | Ok(i) => (i, true), 302 | Err(_) => (0, false), 303 | }; 304 | 305 | let query = path.borrow_query(); 306 | let query_key = query.get_path()?; 307 | 308 | let mut elements = Vec::new(); 309 | let return_list = (query.on && query.all) || (!query.on && path.more); 310 | let only_first = query.on && !query.all; 311 | 312 | bytes = &bytes[1..]; 313 | 314 | let next_path = path.parse_next()?; 315 | 316 | loop { 317 | // index matched 318 | if get_idx && idx == index { 319 | return if path.more { 320 | bytes_get(bytes, &next_path) 321 | } else { 322 | element::read_one(bytes) 323 | }; 324 | } 325 | 326 | let (readed, left) = element::read_one(bytes)?; 327 | bytes = left; 328 | 329 | let mut element = match readed { 330 | None => break, 331 | Some(el) => el, 332 | }; 333 | 334 | // do query filter 335 | if query.on { 336 | if query.has_path() { 337 | match element_ref_get(&element, &query_key)? { 338 | None => continue, 339 | Some(v) => { 340 | if !query.match_element(&v) { 341 | continue; 342 | } 343 | } 344 | } 345 | } else if !query.match_element(&element) { 346 | continue; 347 | } 348 | } 349 | 350 | index += 1; 351 | 352 | if path.more { 353 | match element_get(element, &next_path)? { 354 | Some(el) => element = el, 355 | None => continue, 356 | } 357 | } 358 | 359 | if only_first { 360 | return Ok((Some(element), bytes)); 361 | } 362 | 363 | if return_list { 364 | elements.push(element); 365 | } 366 | } 367 | 368 | if return_list { 369 | Ok((Some(Element::List(elements)), bytes)) 370 | } else if only_first { 371 | Ok((None, bytes)) 372 | } else if path.arrch { 373 | Ok((Some(Element::Count(index)), bytes)) 374 | } else { 375 | Ok((None, bytes)) 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /src/path/builder.rs: -------------------------------------------------------------------------------- 1 | use super::{query::Query, sub_selector::SubSelector, Path}; 2 | 3 | #[derive(Default)] 4 | pub struct Builder<'a> { 5 | ok: Option, 6 | ident: Option<&'a [u8]>, 7 | next: Option<&'a [u8]>, 8 | more: Option, 9 | #[cfg(feature = "wild")] 10 | wild: Option, 11 | arrch: Option, 12 | query: Option>, 13 | selectors: Option>>, 14 | arrsel: Option, 15 | esc: Option, 16 | } 17 | 18 | impl<'a> Builder<'a> { 19 | pub fn build(self) -> crate::Result> { 20 | Ok(Path { 21 | ok: self.ok.unwrap_or_default(), 22 | part: self.ident.unwrap_or_default(), 23 | next: self.next.unwrap_or_default(), 24 | query: self.query, 25 | selectors: self.selectors, 26 | arrsel: self.arrsel.unwrap_or_default(), 27 | more: self.more.unwrap_or_default(), 28 | #[cfg(feature = "wild")] 29 | wild: self.wild.unwrap_or_default(), 30 | arrch: self.arrch.unwrap_or_default(), 31 | esc: self.esc.unwrap_or_default(), 32 | }) 33 | } 34 | } 35 | 36 | impl<'a> Builder<'a> { 37 | pub fn ident(mut self, ident: &'a [u8]) -> Self { 38 | self.ident = Some(ident); 39 | self 40 | } 41 | 42 | pub fn more(mut self, more: bool) -> Self { 43 | self.more = Some(more); 44 | self 45 | } 46 | 47 | pub fn next(mut self, next: &'a [u8]) -> Self { 48 | self.next = Some(next); 49 | self 50 | } 51 | 52 | #[cfg(feature = "wild")] 53 | pub fn wild(mut self, wild: bool) -> Self { 54 | self.wild = Some(wild); 55 | self 56 | } 57 | 58 | pub fn ok(mut self, ok: bool) -> Self { 59 | self.ok = Some(ok); 60 | self 61 | } 62 | 63 | pub fn esc(mut self, esc: bool) -> Self { 64 | self.esc = Some(esc); 65 | self 66 | } 67 | 68 | pub fn arrch(mut self, arrch: bool) -> Self { 69 | self.arrch = Some(arrch); 70 | self 71 | } 72 | 73 | pub fn query(mut self, query: Query<'a>) -> Self { 74 | self.query = Some(query); 75 | self 76 | } 77 | 78 | pub fn selector(mut self, sels: Vec>) -> Self { 79 | self.selectors = Some(sels); 80 | self 81 | } 82 | 83 | pub fn arrsel(mut self, arrsel: bool) -> Self { 84 | self.arrsel = Some(arrsel); 85 | self 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/path/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod parser; 3 | 4 | mod query; 5 | mod sub_selector; 6 | 7 | use std::fmt; 8 | 9 | use builder::Builder; 10 | pub use sub_selector::SubSelector; 11 | 12 | use self::query::{Query, DEFAULT_NONE_QUERY}; 13 | #[cfg(feature = "wild")] 14 | use crate::wild; 15 | use crate::{unescape, util, Result}; 16 | 17 | pub const DEFAULT_NONE_PATH: Path = Path { 18 | ok: false, 19 | part: &[], 20 | next: &[], 21 | more: false, 22 | #[cfg(feature = "wild")] 23 | wild: false, 24 | arrch: false, 25 | 26 | query: None, 27 | selectors: None, 28 | arrsel: false, 29 | esc: false, 30 | }; 31 | 32 | #[derive(Default)] 33 | pub struct Path<'a> { 34 | pub ok: bool, 35 | pub part: &'a [u8], 36 | pub next: &'a [u8], 37 | pub query: Option>, 38 | pub selectors: Option>>, 39 | pub arrsel: bool, 40 | pub more: bool, 41 | #[cfg(feature = "wild")] 42 | pub wild: bool, 43 | pub arrch: bool, 44 | pub esc: bool, 45 | } 46 | 47 | impl<'a> fmt::Debug for Path<'a> { 48 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 49 | write!(f, "") 69 | } 70 | } 71 | 72 | impl<'a> Path<'a> { 73 | pub fn from_slice(v: &'a [u8]) -> Result> { 74 | parser::parse(v) 75 | } 76 | 77 | pub fn builder<'b>() -> Builder<'b> { 78 | Default::default() 79 | } 80 | 81 | pub fn is_match(&self, key: &[u8], key_esc: bool) -> bool { 82 | #[cfg(feature = "wild")] 83 | if self.wild { 84 | return wild::is_match_u8(key, self.part); 85 | return false; 86 | } 87 | 88 | if key_esc { 89 | let s = unescape(key); 90 | s.as_bytes().eq(self.part) 91 | } else if self.esc { 92 | util::equal_escape_u8(key, self.part) 93 | } else { 94 | key.eq(self.part) 95 | } 96 | } 97 | 98 | pub fn parse_next(&self) -> Result> { 99 | if self.next.is_empty() { 100 | Ok(Path::default()) 101 | } else { 102 | Path::from_slice(self.next) 103 | } 104 | } 105 | 106 | pub fn has_query(&self) -> bool { 107 | self.query.is_some() 108 | } 109 | 110 | pub fn borrow_query(&self) -> &Query<'a> { 111 | match self.query { 112 | Some(_) => self.query.as_ref().unwrap(), 113 | None => &DEFAULT_NONE_QUERY, 114 | } 115 | } 116 | 117 | pub fn has_selectors(&self) -> bool { 118 | self.selectors.is_some() 119 | } 120 | 121 | pub fn borrow_selectors(&self) -> &[SubSelector<'a>] { 122 | match self.selectors { 123 | Some(_) => self.selectors.as_ref().unwrap(), 124 | None => &[], 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/path/parser.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | query::{Query, QueryValue}, 3 | sub_selector, Path, 4 | }; 5 | use crate::{element, number::Number, util, Result}; 6 | 7 | pub(super) fn parse(v: &[u8]) -> Result { 8 | if v.is_empty() { 9 | return Ok(Default::default()); 10 | } 11 | 12 | let bytes = v; 13 | let mut current_path = Path::builder(); 14 | let mut depth = 0; 15 | let mut i = 0; 16 | let mut arrch = false; 17 | 18 | while i < bytes.len() { 19 | let &b = unsafe { bytes.get_unchecked(i) }; 20 | 21 | match b { 22 | b'\\' => { 23 | i += 2; 24 | current_path = current_path.esc(true); 25 | continue; 26 | } 27 | b']' | b')' | b'}' => { 28 | if depth > 0 { 29 | depth -= 0; 30 | } 31 | } 32 | b'.' => { 33 | if depth == 0 && i > 0 { 34 | current_path = current_path.ident(&v[..i]); 35 | current_path = current_path.ok(true); 36 | current_path = current_path.more(true); 37 | i += 1; 38 | 39 | current_path = current_path.next(&v[i..]); 40 | 41 | return current_path.build(); 42 | } 43 | } 44 | #[cfg(feature = "wild")] 45 | b'*' | b'?' => current_path = current_path.wild(true), 46 | b'#' => { 47 | if depth == 0 { 48 | current_path = current_path.arrch(true); 49 | arrch = true; 50 | } 51 | } 52 | b @ b'[' | b @ b'(' | b @ b'{' => { 53 | depth += 1; 54 | if depth == 1 { 55 | if arrch { 56 | i += 1; 57 | let (query, offset) = parse_query(&v[i..])?; 58 | if query.on { 59 | i += offset - 1; 60 | } 61 | current_path = current_path.query(query); 62 | 63 | depth = 0; 64 | continue; 65 | } else { 66 | let (selectors, offset, ok) = sub_selector::parse_selectors(&v[i..]); 67 | if ok { 68 | if b != b'{' { 69 | current_path = current_path.arrsel(true); 70 | } 71 | current_path = current_path.selector(selectors); 72 | i += offset - 1; 73 | depth = 0; 74 | } 75 | } 76 | } 77 | } 78 | _ => (), 79 | }; 80 | i += 1; 81 | } 82 | 83 | current_path.ident(v).more(false).ok(true).build() 84 | } 85 | 86 | fn parse_query(v: &[u8]) -> Result<(Query, usize)> { 87 | if v.is_empty() { 88 | return Ok((Query::empty(), 0)); 89 | } 90 | 91 | let bytes = v; 92 | let mut q = Query::empty(); 93 | let mut depth = 1; 94 | let mut end = 0; 95 | 96 | let mut op_exist = false; 97 | let mut op_start = 0; 98 | let mut op_end = 0; 99 | 100 | let mut i = 0; 101 | 102 | while i < bytes.len() { 103 | let &b = unsafe { bytes.get_unchecked(i) }; 104 | 105 | match b { 106 | b'!' | b'=' | b'<' | b'>' | b'%' => { 107 | if depth == 1 { 108 | if !op_exist { 109 | op_exist = true; 110 | op_start = i; 111 | op_end = op_start; 112 | } else { 113 | op_end += 1; 114 | } 115 | } 116 | } 117 | b'[' | b'(' => { 118 | depth += 1; 119 | } 120 | b']' | b')' => { 121 | depth -= 1; 122 | if depth == 0 { 123 | match bytes.get(i + 1) { 124 | Some(b'#') => { 125 | q.set_all(true); 126 | end = i; 127 | i += 1; 128 | } 129 | Some(_) => end = i - 1, 130 | None => end = i, 131 | } 132 | break; 133 | } 134 | } 135 | b' ' => (), 136 | _ => { 137 | if op_exist { 138 | let (val, offset) = parser_query_value(&v[i..])?; 139 | if val.exists() { 140 | q.set_val(val); 141 | } 142 | if offset > 1 { 143 | i += offset - 1; 144 | } 145 | } 146 | } 147 | }; 148 | 149 | i += 1; 150 | } 151 | 152 | q.set_on(true); 153 | 154 | if op_exist { 155 | q.set_path(util::trim_space_u8(&v[..op_start])); 156 | q.set_op(unsafe { std::str::from_utf8_unchecked(v.get_unchecked(op_start..op_end + 1)) }); 157 | } else if end > 0 { 158 | q.set_path(util::trim_space_u8(&v[..end + 1])); 159 | } else { 160 | q.set_path(util::trim_space_u8(v)); 161 | } 162 | 163 | Ok((q, i)) 164 | } 165 | 166 | fn parser_query_value(bytes: &[u8]) -> Result<(QueryValue, usize)> { 167 | if let Some(b) = bytes.first() { 168 | let val = match b { 169 | b't' => { 170 | element::true_u8(bytes)?; 171 | (QueryValue::Boolean(true), 4) 172 | } 173 | b'f' => { 174 | element::false_u8(bytes)?; 175 | (QueryValue::Boolean(false), 5) 176 | } 177 | b'n' => { 178 | element::null_u8(bytes)?; 179 | (QueryValue::Null, 4) 180 | } 181 | b'"' => { 182 | let (s, _, _esc) = element::string_u8(bytes)?; 183 | if s.len() < 2 { 184 | (QueryValue::NotExist, s.len()) 185 | } else { 186 | (QueryValue::String(&s[1..s.len() - 1]), s.len()) 187 | } 188 | } 189 | b'0'..=b'9' | b'-' => { 190 | let (n, _) = element::number_u8(bytes)?; 191 | (QueryValue::F64(Number::from(n).to_f64()), n.len()) 192 | } 193 | _ => (QueryValue::NotExist, 0), 194 | }; 195 | 196 | return Ok(val); 197 | } 198 | 199 | Ok((QueryValue::NotExist, 0)) 200 | } 201 | 202 | #[cfg(test)] 203 | mod tests { 204 | #![allow(unused_variables)] 205 | use super::*; 206 | 207 | #[test] 208 | fn test_invalid_path() { 209 | parse("friends.{}first]".as_bytes()).unwrap(); 210 | } 211 | 212 | #[test] 213 | fn test_fn_parse_from_utf8() { 214 | let v = r#"name"#.as_bytes(); 215 | let p = parse(v); 216 | 217 | let v = r#"#(last=="Murphy")#.first"#.as_bytes(); 218 | let p = parse(v); 219 | 220 | let v = r#"friends.#(first!%"D*")#.last"#.as_bytes(); 221 | let p = parse(v); 222 | 223 | let v = r#"c?ildren.0"#.as_bytes(); 224 | let p = parse(v); 225 | 226 | let v = r#"#(sub_item>7)#.title"#.as_bytes(); 227 | let p = parse(v); 228 | 229 | let v = r#"friends.#(nets."#.as_bytes(); 230 | let p = parse(v); 231 | 232 | let v = r#"friends.#()#"#.as_bytes(); 233 | let p = parse(v); 234 | 235 | let v = "widget.[window,name].#.name".as_bytes(); 236 | let p = parse(v); 237 | 238 | let v = r#"widget.menu.#(title="help")#.title"#.as_bytes(); 239 | let p = parse(v); 240 | } 241 | 242 | #[test] 243 | fn test_fn_parse() { 244 | let v = r#"name"#.as_bytes(); 245 | let p = parse(v); 246 | 247 | let v = r#"#(last=="Murphy")#.first"#.as_bytes(); 248 | let p = parse(v); 249 | 250 | let v = r#"friends.#(first!%"D*")#.last"#.as_bytes(); 251 | let p = parse(v); 252 | 253 | let v = r#"c?ildren.0"#.as_bytes(); 254 | let p = parse(v); 255 | 256 | let v = r#"#(sub_item>7)#.title"#.as_bytes(); 257 | let p = parse(v); 258 | } 259 | 260 | #[test] 261 | fn test_fn_parse_query() { 262 | let v = "first)".as_bytes(); 263 | let q = parse_query(v); 264 | 265 | let v = "first)#".as_bytes(); 266 | let q = parse_query(v); 267 | 268 | let v = r#"first="name")"#.as_bytes(); 269 | let q = parse_query(v); 270 | 271 | let v = r#"nets.#(=="ig"))"#.as_bytes(); 272 | let q = parse_query(v); 273 | 274 | let v = r#"nets.#(=="ig"))#"#.as_bytes(); 275 | let q = parse_query(v); 276 | 277 | let v = r#"=="ig")"#.as_bytes(); 278 | let q = parse_query(v); 279 | 280 | let v = r#"first=)"#.as_bytes(); 281 | let q = parse_query(v); 282 | 283 | let v = r#"sub_item>7)#.title"#.as_bytes(); 284 | let q = parse_query(v); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/path/query.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use super::{parser, Path, DEFAULT_NONE_PATH}; 4 | #[cfg(feature = "wild")] 5 | use crate::wild; 6 | use crate::{element::Element, Result, Value}; 7 | 8 | pub const DEFAULT_NONE_QUERY: Query = Query { 9 | on: false, 10 | path: &[], 11 | key: None, 12 | op: None, 13 | value: None, 14 | all: false, 15 | }; 16 | 17 | #[derive(Debug, PartialEq)] 18 | pub enum QueryValue<'a> { 19 | String(&'a [u8]), 20 | F64(f64), 21 | Boolean(bool), 22 | Null, 23 | NotExist, 24 | } 25 | 26 | impl<'a> QueryValue<'a> { 27 | pub fn exists(&self) -> bool { 28 | *self != QueryValue::NotExist 29 | } 30 | } 31 | 32 | pub struct Query<'a> { 33 | pub on: bool, 34 | pub path: &'a [u8], 35 | pub key: Option>>, 36 | pub op: Option<&'a str>, 37 | pub value: Option>, 38 | pub all: bool, 39 | } 40 | 41 | impl<'a> fmt::Debug for Query<'a> { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | write!(f, "") 61 | } 62 | } 63 | 64 | impl<'a> Query<'a> { 65 | pub fn empty() -> Query<'a> { 66 | Query { 67 | on: false, 68 | path: &[], 69 | key: None, 70 | op: None, 71 | value: None, 72 | all: false, 73 | } 74 | } 75 | 76 | pub fn has_path(&self) -> bool { 77 | !self.path.is_empty() 78 | } 79 | 80 | pub fn get_path(&self) -> Result { 81 | match self.has_path() { 82 | true => parser::parse(self.path), 83 | false => Ok(Path::default()), 84 | } 85 | } 86 | 87 | pub fn has_key(&self) -> bool { 88 | self.key.is_some() 89 | } 90 | 91 | pub fn get_key(&self) -> &Path { 92 | match self.key { 93 | Some(_) => self.key.as_ref().unwrap(), 94 | None => &DEFAULT_NONE_PATH, 95 | } 96 | } 97 | 98 | pub fn set_path(&mut self, v: &'a [u8]) { 99 | self.path = v; 100 | } 101 | 102 | pub fn set_op(&mut self, op: &'a str) { 103 | self.op = Some(op); 104 | } 105 | 106 | pub fn set_val(&mut self, val: QueryValue<'a>) { 107 | self.value = Some(val); 108 | } 109 | 110 | pub fn set_all(&mut self, all: bool) { 111 | self.all = all; 112 | } 113 | 114 | pub fn set_key(&mut self, key: Path<'a>) { 115 | if key.ok { 116 | self.key = Some(Box::new(key)); 117 | } 118 | } 119 | 120 | pub fn set_on(&mut self, on: bool) { 121 | self.on = on; 122 | } 123 | 124 | pub fn match_element(&self, element: &Element) -> bool { 125 | if self.value.is_none() { 126 | return true; 127 | } 128 | 129 | let op = match &self.op { 130 | Some(ref s) => s, 131 | None => return true, 132 | }; 133 | 134 | let target = self.value.as_ref().unwrap(); 135 | let v = element.to_value(); 136 | 137 | match *target { 138 | QueryValue::String(q) => match v { 139 | Value::String(ref s) => match *op { 140 | "==" => s.as_bytes() == q, 141 | "=" => s.as_bytes() == q, 142 | "!=" => s.as_bytes() != q, 143 | ">" => s.as_bytes() > q, 144 | ">=" => s.as_bytes() >= q, 145 | "<" => s.as_bytes() < q, 146 | "<=" => s.as_bytes() <= q, 147 | #[cfg(feature = "wild")] 148 | "%" => wild::is_match_u8(s.as_bytes(), q), 149 | #[cfg(feature = "wild")] 150 | "!%" => !wild::is_match_u8(s.as_bytes(), q), 151 | _ => false, 152 | }, 153 | _ => false, 154 | }, 155 | 156 | QueryValue::F64(q) => match v { 157 | Value::Number(n) => match *op { 158 | "=" => (n.to_f64() - q).abs() < f64::EPSILON, 159 | "==" => (n.to_f64() - q).abs() < f64::EPSILON, 160 | "!=" => (n.to_f64() - q).abs() > f64::EPSILON, 161 | "<" => n.to_f64() < q, 162 | "<=" => n.to_f64() <= q, 163 | ">" => n.to_f64() > q, 164 | ">=" => n.to_f64() >= q, 165 | _ => false, 166 | }, 167 | _ => false, 168 | }, 169 | 170 | QueryValue::Boolean(q) => match v { 171 | Value::Boolean(b) => match *op { 172 | "=" => b == q, 173 | "==" => b == q, 174 | "!=" => b != q, 175 | _ => false, 176 | }, 177 | _ => false, 178 | }, 179 | 180 | QueryValue::Null => match *op { 181 | "=" => v == Value::Null, 182 | "==" => v == Value::Null, 183 | "!=" => v != Value::Null, 184 | _ => false, 185 | }, 186 | _ => false, 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/path/sub_selector.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, str}; 2 | 3 | use crate::{element, util}; 4 | 5 | pub struct SubSelector<'a> { 6 | pub name: &'a [u8], 7 | pub path: &'a [u8], 8 | } 9 | 10 | impl<'a> SubSelector<'a> { 11 | pub fn new(name: &'a [u8], path: &'a [u8]) -> SubSelector<'a> { 12 | SubSelector { name, path } 13 | } 14 | } 15 | 16 | impl<'a> fmt::Debug for SubSelector<'a> { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 | write!(f, "") 22 | } 23 | } 24 | 25 | fn last_of_name(v: &[u8]) -> &[u8] { 26 | // for mut i in (0..v.len()).rev() { 27 | // match v[i] { 28 | // b'\\' => i -= 1, 29 | // b'.' => return &v[i + 1..], 30 | // _ => (), 31 | // } 32 | // } 33 | 34 | if v.is_empty() { 35 | return v; 36 | } 37 | 38 | let mut i = v.len() - 1; 39 | loop { 40 | match v[i] { 41 | b'\\' => i -= 1, 42 | b'.' => return &v[i + 1..], 43 | _ => (), 44 | } 45 | if i == 0 { 46 | break; 47 | } 48 | i -= 1; 49 | } 50 | 51 | v 52 | } 53 | 54 | pub fn parse_selectors(v: &[u8]) -> (Vec, usize, bool) { 55 | let mut i = 0; 56 | let mut depth = 0; 57 | let mut start = 0; 58 | let mut colon = 0; 59 | let mut sels = Vec::new(); 60 | 61 | macro_rules! push_sel { 62 | () => {{ 63 | if start < i { 64 | let sel = if colon == 0 { 65 | let key = last_of_name(&v[start..i]); 66 | SubSelector::new(key, &v[start..i]) 67 | } else { 68 | let key = util::trim_u8(&v[start..colon], b'"'); 69 | SubSelector::new(key, &v[colon + 1..i]) 70 | }; 71 | sels.push(sel); 72 | } 73 | }}; 74 | } 75 | 76 | while i < v.len() { 77 | let &b = unsafe { v.get_unchecked(i) }; 78 | 79 | match b { 80 | b'\\' => { 81 | i += 1; 82 | } 83 | b'"' => { 84 | let input = unsafe { v.get_unchecked(i..) }; 85 | let (a, _, _) = element::string_u8(input).unwrap(); 86 | i += a.len(); 87 | 88 | continue; 89 | } 90 | b':' => { 91 | if depth == 1 { 92 | colon = i; 93 | } 94 | } 95 | b',' => { 96 | if depth == 1 { 97 | push_sel!(); 98 | colon = 0; 99 | start = i; 100 | } 101 | } 102 | b'[' | b'(' | b'{' => { 103 | depth += 1; 104 | if depth == 1 { 105 | start = i + 1; 106 | } 107 | } 108 | 109 | b']' | b')' | b'}' => { 110 | depth -= 1; 111 | if depth == 0 { 112 | push_sel!(); 113 | let length = i; 114 | return (sels, length, true); 115 | } 116 | } 117 | _ => (), 118 | } 119 | 120 | i += 1; 121 | } 122 | 123 | (vec![], 0, false) 124 | } 125 | 126 | #[cfg(test)] 127 | mod tests { 128 | use super::*; 129 | 130 | #[test] 131 | fn test_parse_selectors_from_utf8() { 132 | #![allow(unused_variables)] 133 | let path = r#"{name.first,age,murphys:friends.#(last="Murphy")#.first}"#; 134 | let (sels, length, ok) = parse_selectors(path.as_bytes()); 135 | 136 | let path = r#"[name,a]"#; 137 | let (sels, length, ok) = parse_selectors(path.as_bytes()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/unescape.rs: -------------------------------------------------------------------------------- 1 | use std::u32; 2 | 3 | pub fn unescape(v: &[u8]) -> String { 4 | let mut s = Vec::with_capacity(v.len()); 5 | let mut i = 0; 6 | while i < v.len() { 7 | match v[i] { 8 | b'\\' => { 9 | i += 1; 10 | if i >= v.len() { 11 | break; 12 | } 13 | 14 | match v[i] { 15 | b'"' | b'\\' | b'/' => s.push(v[i]), 16 | // https://github.com/rust-lang/rfcs/issues/751 17 | b'b' => s.push(0x08), 18 | b'f' => s.push(0x0C), 19 | b'n' => s.push(b'\n'), 20 | b'r' => s.push(b'\r'), 21 | b't' => s.push(b'\t'), 22 | b'u' => { 23 | let j = i; 24 | if i + 5 > v.len() { 25 | for c in &v[j - 1..] { 26 | s.push(*c); 27 | } 28 | break; 29 | } 30 | 31 | let mut codepoint = u8s_to_u32(&v[i + 1..i + 5]); 32 | let mut parse_ok = false; 33 | i += 5; 34 | 35 | let mut is_surrogate_pair = true; 36 | // UTF-16 surrogate pairs 37 | // Surrogates are characters in the Unicode range U+D800—U+DFFF (2,048 code points): it is also the Unicode category “surrogate” (Cs). The range is composed of two parts: 38 | // U+D800—U+DBFF (1,024 code points): high surrogates 39 | // U+DC00—U+DFFF (1,024 code points): low surrogates 40 | match codepoint { 41 | 0x0000..=0xD7FF => (), 42 | 0xD800..=0xDBFF => { 43 | if i + 5 < v.len() && v[i] == b'\\' { 44 | codepoint -= 0xD800; 45 | codepoint <<= 10; 46 | let lower = u8s_to_u32(&v[i + 2..i + 6]); 47 | if let 0xDC00..=0xDFFF = lower { 48 | codepoint = (codepoint | (lower - 0xDC00)) + 0x010000; 49 | } 50 | i += 6; 51 | } else { 52 | i += 2; 53 | } 54 | } 55 | 0xE000..=0xFFFF => (), 56 | _ => is_surrogate_pair = false, 57 | }; 58 | 59 | if is_surrogate_pair { 60 | parse_ok = write_codepoint(codepoint, &mut s); 61 | } 62 | 63 | if !parse_ok { 64 | for c in &v[j - 1..i] { 65 | s.push(*c) 66 | } 67 | // if i> j+6 { 68 | // s.push(b'\\'); 69 | // for c in &v[j+5..i] { 70 | // s.push(*c) 71 | // } 72 | // } 73 | } 74 | i -= 1; 75 | } 76 | b => { 77 | s.push(b'\\'); 78 | s.push(b); 79 | } 80 | } 81 | } 82 | c => s.push(c), 83 | } 84 | i += 1; 85 | } 86 | 87 | unsafe { String::from_utf8_unchecked(s) } 88 | } 89 | 90 | fn u8s_to_u32(v: &[u8]) -> u32 { 91 | u8_to_u32(v[0]) << 12 | u8_to_u32(v[1]) << 8 | u8_to_u32(v[2]) << 4 | u8_to_u32(v[3]) 92 | } 93 | 94 | fn u8_to_u32(b: u8) -> u32 { 95 | let r = match b { 96 | b'0'..=b'9' => (b - b'0'), 97 | b'a'..=b'f' => (b + 10 - b'a'), 98 | b'A'..=b'F' => (b + 10 - b'A'), 99 | _ => panic!("unexpected byte"), 100 | }; 101 | r as u32 102 | } 103 | 104 | fn write_codepoint(codepoint: u32, buffer: &mut Vec) -> bool { 105 | match codepoint { 106 | 0x0000..=0x007F => buffer.push(codepoint as u8), 107 | 0x0080..=0x07FF => buffer.extend_from_slice(&[ 108 | (((codepoint >> 6) as u8) & 0x1F) | 0xC0, 109 | ((codepoint as u8) & 0x3F) | 0x80, 110 | ]), 111 | 0x0800..=0xFFFF => buffer.extend_from_slice(&[ 112 | (((codepoint >> 12) as u8) & 0x0F) | 0xE0, 113 | (((codepoint >> 6) as u8) & 0x3F) | 0x80, 114 | ((codepoint as u8) & 0x3F) | 0x80, 115 | ]), 116 | 0x10000..=0x10FFFF => buffer.extend_from_slice(&[ 117 | (((codepoint >> 18) as u8) & 0x07) | 0xF0, 118 | (((codepoint >> 12) as u8) & 0x3F) | 0x80, 119 | (((codepoint >> 6) as u8) & 0x3F) | 0x80, 120 | ((codepoint as u8) & 0x3F) | 0x80, 121 | ]), 122 | _ => return false, 123 | }; 124 | 125 | true 126 | } 127 | 128 | #[cfg(test)] 129 | mod tests { 130 | use super::*; 131 | #[test] 132 | fn test_unescape() { 133 | println!( 134 | "{}", 135 | unescape(r#"\ud83d\udd13, \ud83c\udfc3 OK: \u2764\ufe0f"#.as_bytes()) 136 | ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | pub fn trim_space_u8(v: &[u8]) -> &[u8] { 2 | trim_u8(v, b' ') 3 | } 4 | 5 | pub fn trim_u8(v: &[u8], b: u8) -> &[u8] { 6 | let length = v.len(); 7 | let mut i = 0; 8 | let mut j = length; 9 | if length == 0 { 10 | return v; 11 | } 12 | 13 | while i < j - 1 { 14 | if v[i] == b { 15 | i += 1; 16 | } else { 17 | break; 18 | } 19 | } 20 | 21 | while j > i { 22 | if v[j - 1] == b { 23 | j -= 1; 24 | } else { 25 | break; 26 | } 27 | } 28 | 29 | &v[i..j] 30 | } 31 | 32 | pub fn equal_escape_u8(a: &[u8], b: &[u8]) -> bool { 33 | // if !a.contains(&'\\') && !b.contains(&'\\') { 34 | // return a == b 35 | // } 36 | 37 | let mut i = 0; 38 | let mut j = 0; 39 | 40 | while i < a.len() && j < b.len() { 41 | if a[i] == b'\\' { 42 | i += 1 43 | } 44 | 45 | if b[j] == b'\\' { 46 | j += 1 47 | } 48 | 49 | if i >= a.len() || j >= b.len() { 50 | break; 51 | } 52 | 53 | if a[i] != b[j] { 54 | return false; 55 | } 56 | 57 | i += 1; 58 | j += 1; 59 | } 60 | 61 | !(j != b.len() || i != a.len()) 62 | } 63 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, collections::HashMap, fmt, fmt::Formatter, str}; 2 | 3 | use crate::{number::Number, parser, path::Path, Result}; 4 | 5 | /// Represents JSON valuue. 6 | #[derive(PartialEq, Eq, Clone)] 7 | pub enum Value<'a> { 8 | /// Represents a JSON String. 9 | String(Cow<'a, str>), 10 | /// Respesents a JSON number. 11 | Number(Number<'a>), 12 | /// Respesents a JSON number. 13 | Usize(usize), 14 | /// Respesents a JSON object. 15 | Object(Cow<'a, str>), 16 | /// Respesents a JSON array. 17 | Array(Cow<'a, str>), 18 | /// Respesents a JSON boolean. 19 | Boolean(bool), 20 | /// Respesents a JSON null value. 21 | Null, 22 | } 23 | 24 | impl<'a> fmt::Debug for Value<'a> { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | match self { 27 | Value::String(s) => write!(f, r#""{}""#, s), 28 | Value::Number(n) => write!(f, "{}", n.as_str()), 29 | Value::Usize(n) => write!(f, "{}", n), 30 | Value::Object(s) | Value::Array(s) => write!(f, "{}", s), 31 | Value::Boolean(b) => write!(f, "{}", b), 32 | Value::Null => write!(f, "null"), 33 | } 34 | } 35 | } 36 | 37 | impl<'a> Value<'a> { 38 | /// Get sub value from a JSON array or map. 39 | /// About path syntax, see [here](index.html#syntax). 40 | /// For more detail, see [`get`](fn.get.html). 41 | /// ``` 42 | /// use ajson::{Result, Value}; 43 | /// fn main() -> Result<()> { 44 | /// let v = Value::Array("[1,2,3]".into()); 45 | /// let first_num = v.get("0")?.unwrap(); 46 | /// assert_eq!(first_num, 1_i64); 47 | /// Ok(()) 48 | /// } 49 | /// ``` 50 | pub fn get(&self, path: &'a str) -> Result> { 51 | match self { 52 | Value::Array(s) | Value::Object(s) => { 53 | let p = Path::from_slice(path.as_ref())?; 54 | let (a, _left) = parser::bytes_get(s.as_bytes(), &p)?; 55 | Ok(a.map(|el| el.to_value())) 56 | } 57 | _ => Ok(None), 58 | } 59 | } 60 | } 61 | 62 | impl<'a> Value<'a> { 63 | /// Returns true if the `Value` is a JSON string. 64 | /// ``` 65 | /// let v = ajson::get(r#"{"name":"ajson"}"#, "name").unwrap().unwrap(); 66 | /// assert!(v.is_string()); 67 | /// ``` 68 | pub fn is_string(&self) -> bool { 69 | matches!(self, Value::String(_)) 70 | } 71 | 72 | pub fn is_number(&self) -> bool { 73 | matches!(self, Value::Number(_)) 74 | } 75 | 76 | pub fn is_array(&self) -> bool { 77 | matches!(self, Value::Array(_)) 78 | } 79 | 80 | pub fn is_object(&self) -> bool { 81 | matches!(self, Value::Object(_)) 82 | } 83 | 84 | pub fn is_bool(&self) -> bool { 85 | matches!(self, Value::Boolean(_)) 86 | } 87 | 88 | pub fn is_null(&self) -> bool { 89 | matches!(self, Value::Null) 90 | } 91 | } 92 | 93 | impl<'a> std::fmt::Display for Value<'a> { 94 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 95 | match self { 96 | Value::String(ref s) => write!(f, "{}", s), 97 | Value::Number(number) => write!(f, "{}", number.as_str()), 98 | Value::Boolean(true) => write!(f, "true"), 99 | Value::Boolean(false) => write!(f, "false"), 100 | Value::Object(ref s) => write!(f, "{}", s), 101 | Value::Array(ref s) => write!(f, "{}", s), 102 | Value::Usize(u) => write!(f, "{}", u), 103 | Value::Null => write!(f, "null"), 104 | } 105 | } 106 | } 107 | 108 | impl<'a> Value<'a> { 109 | pub fn as_str(&self) -> Option<&str> { 110 | match self { 111 | Value::String(s) => Some(s), 112 | _ => None, 113 | } 114 | } 115 | 116 | pub fn as_f64(&self) -> Option { 117 | match self { 118 | Value::Number(number) => Some(number.to_f64()), 119 | Value::Usize(n) => Some(*n as f64), 120 | _ => None, 121 | } 122 | } 123 | 124 | pub fn as_u64(&self) -> Option { 125 | match self { 126 | Value::Number(number) => Some(number.to_u64()), 127 | Value::Usize(n) => Some(*n as u64), 128 | _ => None, 129 | } 130 | } 131 | 132 | pub fn as_i64(&self) -> Option { 133 | match self { 134 | Value::Number(number) => Some(number.to_i64()), 135 | Value::Usize(n) => Some(*n as i64), 136 | _ => None, 137 | } 138 | } 139 | 140 | pub fn as_bool(&self) -> Option { 141 | match *self { 142 | Value::Boolean(b) => Some(b), 143 | _ => None, 144 | } 145 | } 146 | 147 | pub fn as_vec(&self) -> Option> { 148 | match self { 149 | Value::Array(s) => parser::bytes_to_vec(s.as_bytes()).ok(), 150 | _ => None, 151 | } 152 | } 153 | 154 | pub fn as_object(&self) -> Option> { 155 | match self { 156 | Value::Object(s) => parser::bytes_to_map(s.as_bytes()).ok(), 157 | _ => None, 158 | } 159 | } 160 | } 161 | 162 | fn eq_f64(value: &Value, other: f64) -> bool { 163 | value.as_f64().map_or(false, |i| i == other) 164 | } 165 | 166 | fn eq_i64(value: &Value, other: i64) -> bool { 167 | value.as_i64().map_or(false, |i| i == other) 168 | } 169 | 170 | fn eq_u64(value: &Value, other: u64) -> bool { 171 | value.as_u64().map_or(false, |i| i == other) 172 | } 173 | 174 | fn eq_bool(value: &Value, other: bool) -> bool { 175 | value.as_bool().map_or(false, |i| i == other) 176 | } 177 | 178 | fn eq_str(value: &Value, other: &str) -> bool { 179 | value.as_str().map_or(false, |i| i == other) 180 | } 181 | 182 | impl<'a> PartialEq for Value<'a> { 183 | fn eq(&self, other: &str) -> bool { 184 | eq_str(self, other) 185 | } 186 | } 187 | 188 | impl<'a> PartialEq<&'a str> for Value<'a> { 189 | fn eq(&self, other: &&str) -> bool { 190 | eq_str(self, *other) 191 | } 192 | } 193 | 194 | impl<'a> PartialEq> for str { 195 | fn eq(&self, other: &Value) -> bool { 196 | eq_str(other, self) 197 | } 198 | } 199 | 200 | impl<'a> PartialEq> for &'a str { 201 | fn eq(&self, other: &Value) -> bool { 202 | eq_str(other, *self) 203 | } 204 | } 205 | 206 | impl<'a> PartialEq for Value<'a> { 207 | fn eq(&self, other: &String) -> bool { 208 | eq_str(self, other.as_str()) 209 | } 210 | } 211 | 212 | impl<'a> PartialEq> for String { 213 | fn eq(&self, other: &Value) -> bool { 214 | eq_str(other, self.as_str()) 215 | } 216 | } 217 | 218 | macro_rules! partialeq_numeric { 219 | ($($eq:ident [$($ty:ty)*])*) => { 220 | $($( 221 | impl<'a> PartialEq<$ty> for Value<'a> { 222 | fn eq(&self, other: &$ty) -> bool { 223 | $eq(self, *other as _) 224 | } 225 | } 226 | 227 | impl<'a> PartialEq> for $ty { 228 | fn eq(&self, other: &Value) -> bool { 229 | $eq(other, *self as _) 230 | } 231 | } 232 | 233 | impl<'a> PartialEq<$ty> for &'a Value<'a> { 234 | fn eq(&self, other: &$ty) -> bool { 235 | $eq(*self, *other as _) 236 | } 237 | } 238 | 239 | impl<'a> PartialEq<$ty> for &'a mut Value<'a> { 240 | fn eq(&self, other: &$ty) -> bool { 241 | $eq(*self, *other as _) 242 | } 243 | } 244 | )*)* 245 | } 246 | } 247 | 248 | partialeq_numeric! { 249 | eq_i64[i8 i16 i32 i64 isize] 250 | eq_u64[u8 u16 u32 u64 usize] 251 | eq_f64[f32 f64] 252 | eq_bool[bool] 253 | } 254 | -------------------------------------------------------------------------------- /src/wild.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | use regex::Regex; 4 | 5 | pub fn is_match(k1: &str, pattern: &str) -> bool { 6 | let pattern = &pattern.replace("?", ".").replace("*", ".+?"); 7 | Regex::new(pattern).unwrap().is_match(k1) 8 | } 9 | 10 | pub fn is_match_u8(k1: &[u8], pattern: &[u8]) -> bool { 11 | let key = str::from_utf8(k1).unwrap(); 12 | let pat = str::from_utf8(pattern).unwrap(); 13 | let pat = &pat.replace("?", ".").replace("*", ".+?"); 14 | Regex::new(pat).unwrap().is_match(key) 15 | } 16 | -------------------------------------------------------------------------------- /tests/test_ajson.rs: -------------------------------------------------------------------------------- 1 | extern crate ajson; 2 | extern crate json; 3 | extern crate serde_json; 4 | 5 | use ajson::{get, parse, Result, Value}; 6 | 7 | // #[test] 8 | // fn test_json_rs_unicode() { 9 | 10 | // use serde_json::Value; 11 | 12 | // let data = r#"{"IdentityData":{"GameInstanceId":634866135153775564}}"#; 13 | // // let a = &json::parse(data).unwrap(); 14 | // let a = &serde_json::from_str::(data).unwrap(); 15 | // println!("{}", a["IdentityData"]["GameInstanceId"].to_u64().unwrap()); 16 | // println!("{}", a["IdentityData"]["GameInstanceId"].to_i64().unwrap()); 17 | // println!("{}", a["IdentityData"]["GameInstanceId"].to_f64().unwrap()); 18 | 19 | // let data = r#"{"IdentityData":{"GameInstanceId":634866135153775564.88172}}"#; 20 | // let a = &serde_json::from_str::(data).unwrap(); 21 | // // println!("{}", a["IdentityData"]["GameInstanceId"].to_u64().unwrap()); 22 | // // println!("{}", a["IdentityData"]["GameInstanceId"].to_i64().unwrap()); 23 | // println!("{}", a["IdentityData"]["GameInstanceId"].to_f64().unwrap()); 24 | 25 | // let data = r#" 26 | // { 27 | // "min_uint64": 0, 28 | // "max_uint64": 18446744073709551615, 29 | // "overflow_uint64": 18446744073709551616, 30 | // "min_int64": -9223372036854775808, 31 | // "max_int64": 9223372036854775807, 32 | // "overflow_int64": 9223372036854775808, 33 | // "min_uint53": 0, 34 | // "max_uint53": 4503599627370495, 35 | // "overflow_uint53": 4503599627370496, 36 | // "min_int53": -2251799813685248, 37 | // "max_int53": 2251799813685247, 38 | // "overflow_int53": 2251799813685248 39 | // } 40 | // "#; 41 | 42 | // // let b = &json::parse(data).unwrap(); 43 | // let b = &serde_json::from_str::(data).unwrap(); 44 | // assert_eq!(b["min_uint53"].to_u64().unwrap(), 0); 45 | // assert_eq!(b["max_uint53"].to_u64().unwrap(), 4503599627370495); 46 | // assert_eq!(b["overflow_uint53"].to_i64().unwrap(), 4503599627370496); 47 | // assert_eq!(b["min_int53"].to_i64().unwrap(), -2251799813685248); 48 | // assert_eq!(b["max_int53"].to_i64().unwrap(), 2251799813685247); 49 | // assert_eq!(b["overflow_int53"].to_i64().unwrap(), 2251799813685248); 50 | // assert_eq!(b["min_uint64"].to_u64().unwrap(), 0); 51 | // assert_eq!(b["max_uint64"].to_u64().unwrap(), 18446744073709551615); 52 | 53 | // assert_eq!(b["overflow_uint64"].to_i64().unwrap(), 0); 54 | // assert_eq!(b["min_int64"].to_i64().unwrap(), -9223372036854775808); 55 | // assert_eq!(b["max_int64"].to_i64().unwrap(), 9223372036854775807); 56 | 57 | // assert_eq!(b["overflow_int64"].to_i64().unwrap(), -9223372036854775808); 58 | // } 59 | 60 | // fn get(json: &str, path: &str) -> Option { 61 | // match env::var("GETTER_FROM_READ") { 62 | // Ok(open) => { 63 | // if open.len() > 0 { 64 | // println!("get from read"); 65 | // let mut g = Getter::new_from_read(json.as_bytes()); 66 | // g.get(path) 67 | // } else { 68 | // println!("get from str"); 69 | // ajson_get(json, path) 70 | // } 71 | // } 72 | // _ => { 73 | // println!("get from str"); 74 | // ajson_get(json, path) 75 | // } 76 | // } 77 | // } 78 | 79 | static BASIC_JSON: &'static str = r#" 80 | {"age":100, "name":{"here":"B\\\"R"}, 81 | "noop":{"what is a wren?":"a bird"}, 82 | "happy":true,"immortal":false, 83 | "items":[1,2,3,{"tags":[1,2,3],"points":[[1,2],[3,4]]},4,5,6,7], 84 | "arr":["1",2,"3",{"hello":"world"},"4",5], 85 | "vals":[1,2,3,{"sadf":sdx"asdf"}],"name":{"first":"tom","last":null}, 86 | "created":"2014-05-16T08:28:06.989Z", 87 | "loggy":{ 88 | "programmers": [ 89 | { 90 | "firstName": "Brett", 91 | "lastName": "McLaughlin", 92 | "email": "aaaa", 93 | "tag": "good" 94 | }, 95 | { 96 | "firstName": "Jason", 97 | "lastName": "Hunter", 98 | "email": "bbbb", 99 | "tag": "bad" 100 | }, 101 | { 102 | "firstName": "Elliotte", 103 | "lastName": "Harold", 104 | "email": "cccc", 105 | "tag":, "good" 106 | }, 107 | { 108 | "firstName": 1002.3, 109 | "age": 101 110 | } 111 | ] 112 | }, 113 | "lastly":{"yay":"final"} 114 | } 115 | "#; 116 | 117 | static BASIC_JSON2: &'static str = r#" 118 | { 119 | "name": {"first": "Tom", "last": "Anderson"}, 120 | "age":37, 121 | "children": ["Sara","Alex","Jack"], 122 | "fav.movie": "Deer Hunter", 123 | "friends": [ 124 | {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]}, 125 | {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]}, 126 | {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]} 127 | ] 128 | } 129 | "#; 130 | 131 | // name.last >> "Anderson" 132 | // age >> 37 133 | // children >> ["Sara","Alex","Jack"] 134 | // children.# >> 3 135 | // children.1 >> "Alex" 136 | // child*.2 >> "Jack" 137 | // c?ildren.0 >> "Sara" 138 | // fav\.movie >> "Deer Hunter" 139 | // friends.#.first >> ["Dale","Roger","Jane"] 140 | // friends.1.last >> "Craig" 141 | #[test] 142 | fn test_example() -> Result<()> { 143 | let r = parse(BASIC_JSON2)?.unwrap(); 144 | assert_eq!(r.get("name.last")?.unwrap(), "Anderson"); 145 | assert_eq!(r.get("age")?.unwrap(), 37); 146 | assert_eq!( 147 | r.get("children")?.unwrap().as_vec().unwrap(), 148 | vec!["Sara", "Alex", "Jack"] 149 | ); 150 | assert_eq!(r.get("children.#")?.unwrap(), 3 as u64); 151 | assert_eq!(r.get("children.1")?.unwrap(), "Alex"); 152 | assert_eq!(r.get("child*.2")?.unwrap(), "Jack"); 153 | assert_eq!(r.get("c?ildren.0")?.unwrap(), "Sara"); 154 | assert_eq!(r.get("fav\\.movie")?.unwrap(), "Deer Hunter"); 155 | assert_eq!(r.get("friends.1.last")?.unwrap(), "Craig"); 156 | assert_eq!( 157 | r.get("friends.#.first")?.unwrap().as_vec().unwrap(), 158 | vec!["Dale", "Roger", "Jane"] 159 | ); 160 | 161 | Ok(()) 162 | } 163 | 164 | // friends.#(last=="Murphy").first >> "Dale" 165 | // friends.#(last=="Murphy")#.first >> ["Dale","Jane"] 166 | // friends.#(age>45)#.last >> ["Craig","Murphy"] 167 | // friends.#(first%"D*").last >> "Murphy" 168 | // friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"] 169 | #[test] 170 | fn test_query_example() -> Result<()> { 171 | let r = parse(BASIC_JSON2)?.unwrap(); 172 | assert_eq!( 173 | r.get(r#"friends.#(last=="Murphy").first"#)?.unwrap(), 174 | "Dale" 175 | ); 176 | assert_eq!( 177 | r.get(r#"friends.#(last=="Murphy")#.first"#)? 178 | .unwrap() 179 | .as_vec() 180 | .unwrap(), 181 | vec!["Dale", "Jane"] 182 | ); 183 | assert_eq!( 184 | r.get(r#"friends.#(age>45)#.last"#)? 185 | .unwrap() 186 | .as_vec() 187 | .unwrap(), 188 | vec!["Craig", "Murphy"] 189 | ); 190 | assert_eq!(r.get(r#"friends.#(first%"D*").last"#)?.unwrap(), "Murphy"); 191 | assert_eq!( 192 | r.get(r#"friends.#(nets.#(=="fb"))#.first"#)? 193 | .unwrap() 194 | .as_vec() 195 | .unwrap(), 196 | vec!["Dale", "Roger"] 197 | ); 198 | Ok(()) 199 | } 200 | 201 | #[test] 202 | fn test_basic() -> Result<()> { 203 | let r = ajson::parse(BASIC_JSON).unwrap().unwrap(); 204 | println!( 205 | "{:?}", 206 | r.get(r#"loggy.programmers.#[tag="good"].firstName"#) 207 | .unwrap() 208 | ); 209 | assert_eq!( 210 | "Brett", 211 | r.get(r#"loggy.programmers.#[tag="good"].firstName"#) 212 | .unwrap() 213 | .unwrap() 214 | .as_str() 215 | .unwrap() 216 | ); 217 | assert_eq!( 218 | r.get(r#"loggy.programmers.#[tag="good"]#.firstName"#) 219 | .unwrap() 220 | .unwrap() 221 | .as_vec() 222 | .unwrap(), 223 | vec!["Brett", "Elliotte"] 224 | ); 225 | 226 | Ok(()) 227 | } 228 | 229 | #[test] 230 | fn test_basic_2() -> Result<()> { 231 | let r = ajson::parse(BASIC_JSON)?.unwrap(); 232 | let mut mtok = r 233 | .get(r#"loggy.programmers.#[age==101].firstName"#)? 234 | .unwrap(); 235 | assert_eq!(mtok, 1002.3); 236 | mtok = r 237 | .get(r#"loggy.programmers.#[firstName != "Brett"].firstName"#)? 238 | .unwrap(); 239 | assert_eq!(mtok, "Jason"); 240 | 241 | mtok = r 242 | .get(r#"loggy.programmers.#[firstName % "Bre*"].email"#)? 243 | .unwrap(); 244 | assert_eq!(mtok, "aaaa"); 245 | 246 | mtok = r 247 | .get(r#"loggy.programmers.#[firstName !% "Bre*"].email"#)? 248 | .unwrap(); 249 | assert_eq!(mtok, "bbbb"); 250 | 251 | mtok = r 252 | .get(r#"loggy.programmers.#[firstName == "Brett"].email"#)? 253 | .unwrap(); 254 | assert_eq!(mtok, "aaaa"); 255 | 256 | mtok = r.get("loggy")?.unwrap(); 257 | assert!(mtok.is_object()); 258 | println!("{:?}", mtok.as_object()); 259 | assert_eq!(mtok.as_object().unwrap().len(), 1); 260 | 261 | let programmers = &mtok.as_object().unwrap()["programmers"]; 262 | assert_eq!( 263 | programmers.as_vec().unwrap()[1].as_object().unwrap()["firstName"], 264 | "Jason" 265 | ); 266 | 267 | Ok(()) 268 | } 269 | 270 | #[test] 271 | fn test_basic_3() -> Result<()> { 272 | let t = ajson::parse(BASIC_JSON)?.unwrap(); 273 | let json = "-102"; 274 | let t = parse(json)?.unwrap(); 275 | assert_eq!(t, -102); 276 | 277 | let json = "102"; 278 | let t = parse(json)?.unwrap(); 279 | assert_eq!(t, 102); 280 | 281 | let json = "102.2"; 282 | let t = parse(json)?.unwrap(); 283 | assert_eq!(t, 102.2); 284 | 285 | let json = r#""hello""#; 286 | let t = parse(json)?.unwrap(); 287 | assert_eq!(t, "hello"); 288 | 289 | let json = r#""\"he\nllo\"""#; 290 | let t = parse(json)?.unwrap(); 291 | assert_eq!(t, "\"he\nllo\""); 292 | 293 | let t = parse(BASIC_JSON)?.unwrap(); 294 | let t = t.get("loggy.programmers.#.firstName")?.unwrap(); 295 | assert_eq!(t.as_vec().unwrap().len(), 4); 296 | assert_eq!(t.as_vec().unwrap()[..3], ["Brett", "Jason", "Elliotte"]); 297 | 298 | let t = parse(BASIC_JSON)?.unwrap(); 299 | let t = t.get("loggy.programmers.#.asd")?.unwrap(); 300 | assert!(t.is_array()); 301 | assert_eq!(t.as_vec().unwrap().len(), 0); 302 | Ok(()) 303 | } 304 | 305 | #[test] 306 | fn test_basic_4() -> Result<()> { 307 | assert_eq!(get(&BASIC_JSON, "items.3.tags.#")?.unwrap(), 3 as f64); 308 | assert_eq!(get(&BASIC_JSON, "items.3.points.1.#")?.unwrap(), 2 as f64); 309 | assert_eq!(get(&BASIC_JSON, "items.#")?.unwrap(), 8 as f64); 310 | assert_eq!(get(&BASIC_JSON, "vals.#")?.unwrap(), 4 as f64); 311 | assert!(!get(&BASIC_JSON, "name.last")?.is_some()); 312 | assert_eq!(get(&BASIC_JSON, "name.here")?.unwrap(), "B\\\"R"); 313 | 314 | assert_eq!(get(&BASIC_JSON, "arr.#")?.unwrap(), 6 as f64); 315 | assert_eq!(get(&BASIC_JSON, "arr.3.hello")?.unwrap(), "world"); 316 | // Need to Fix 317 | // assert_eq!(get(&BASIC_JSON, "name.first"), "tom"); 318 | // assert_eq!(get(&BASIC_JSON, "name.last").unwrap(), ""); 319 | // Need to Fix 320 | // assert!(get(&BASIC_JSON, "name.last").is_null()); 321 | Ok(()) 322 | } 323 | 324 | #[test] 325 | fn test_basic_5() -> Result<()> { 326 | assert_eq!(get(&BASIC_JSON, "age")?.unwrap(), 100); 327 | assert_eq!(get(&BASIC_JSON, "happy")?.unwrap(), true); 328 | assert_eq!(get(&BASIC_JSON, "immortal")?.unwrap(), false); 329 | 330 | let t = get(&BASIC_JSON, "noop")?.unwrap(); 331 | let m = t.as_object().unwrap(); 332 | assert_eq!(m.len(), 1); 333 | assert_eq!(m["what is a wren?"], "a bird"); 334 | 335 | let r = parse(&BASIC_JSON)?.unwrap(); 336 | assert_eq!( 337 | r.as_object().unwrap()["loggy"].as_object().unwrap()["programmers"] 338 | .as_vec() 339 | .unwrap()[1] 340 | .as_object() 341 | .unwrap()["firstName"], 342 | "Jason" 343 | ); 344 | 345 | Ok(()) 346 | } 347 | 348 | #[test] 349 | fn test_is_array_is_object() -> Result<()> { 350 | let r = parse(BASIC_JSON)?.unwrap(); 351 | let mut mtok = r.get("loggy")?.unwrap(); 352 | assert!(mtok.is_object()); 353 | assert!(!mtok.is_array()); 354 | 355 | mtok = r.get("loggy.programmers")?.unwrap(); 356 | assert!(!mtok.is_object()); 357 | assert!(mtok.is_array()); 358 | 359 | mtok = r.get(r#"loggy.programmers.#[tag="good"]#.first"#)?.unwrap(); 360 | assert!(mtok.is_array()); 361 | 362 | mtok = r.get("loggy.programmers.0.firstName")?.unwrap(); 363 | println!("{:?}", mtok.as_object()); 364 | assert!(!mtok.is_object()); 365 | assert!(!mtok.is_array()); 366 | 367 | Ok(()) 368 | } 369 | 370 | #[test] 371 | fn test_plus_53_bit_ints() -> Result<()> { 372 | let json = r#"{"IdentityData":{"GameInstanceId":634866135153775564}}"#; 373 | let v = get(&json, "IdentityData.GameInstanceId")?.unwrap(); 374 | assert_eq!(v, 634866135153775564 as u64); 375 | assert_eq!(v, 634866135153775564 as i64); 376 | assert_eq!(v, 634866135153775616.0 as f64); 377 | 378 | let json = r#"{"IdentityData":{"GameInstanceId":634866135153775564.88172}}"#; 379 | let v = get(&json, "IdentityData.GameInstanceId")?.unwrap(); 380 | assert_eq!(v, 634866135153775564 as u64); 381 | assert_eq!(v, 634866135153775564 as i64); 382 | assert_eq!(v, 634866135153775616.88172 as f64); 383 | 384 | let json = r#" 385 | { 386 | "min_uint64": 0, 387 | "max_uint64": 18446744073709551615, 388 | "overflow_uint64": 18446744073709551616, 389 | "min_int64": -9223372036854775808, 390 | "max_int64": 9223372036854775807, 391 | "overflow_int64": 9223372036854775808, 392 | "min_uint53": 0, 393 | "max_uint53": 4503599627370495, 394 | "overflow_uint53": 4503599627370496, 395 | "min_int53": -2251799813685248, 396 | "max_int53": 2251799813685247, 397 | "overflow_int53": 2251799813685248 398 | } 399 | "#; 400 | 401 | assert_eq!(get(json, "min_uint53")?.unwrap(), 0); 402 | assert_eq!(get(&json, "max_uint53")?.unwrap(), 4503599627370495 as u64); 403 | assert_eq!( 404 | get(&json, "overflow_uint53")?.unwrap(), 405 | 4503599627370496 as i64 406 | ); 407 | assert_eq!(get(&json, "min_int53")?.unwrap(), -2251799813685248 as i64); 408 | assert_eq!(get(&json, "max_int53")?.unwrap(), 2251799813685247 as i64); 409 | assert_eq!( 410 | get(&json, "overflow_int53")?.unwrap(), 411 | 2251799813685248 as i64 412 | ); 413 | assert_eq!(get(&json, "min_uint64")?.unwrap(), 0); 414 | assert_eq!( 415 | get(&json, "max_uint64")?.unwrap(), 416 | 18446744073709551615 as u64 417 | ); 418 | 419 | assert_eq!(get(&json, "overflow_uint64")?.unwrap(), 0); 420 | assert_eq!( 421 | get(&json, "min_int64")?.unwrap(), 422 | -9223372036854775808 as i64 423 | ); 424 | assert_eq!( 425 | get(&json, "max_int64")?.unwrap(), 426 | 9223372036854775807 as i64 427 | ); 428 | 429 | assert_eq!(get(&json, "overflow_int64")?.unwrap(), 0); 430 | 431 | Ok(()) 432 | } 433 | 434 | #[test] 435 | fn test_unicode() -> Result<()> { 436 | let json = r#"{"key":0,"的情况下解":{"key":1,"的情况":2}}"#; 437 | let r = parse(json)?.unwrap(); 438 | println!("{:?}", r.as_object()); 439 | println!("{:?}", r.get("的情况下解")?.unwrap().as_object()); 440 | assert_eq!(r.get("的情况下解.key")?.unwrap(), 1.0); 441 | assert_eq!(r.get("的情况下解.的情况")?.unwrap(), 2.0); 442 | assert_eq!(r.get("的情况下解.的?况")?.unwrap(), 2.0); 443 | assert_eq!(r.get("的情况下解.的?*")?.unwrap(), 2.0); 444 | assert_eq!(r.get("的情况下解.*?况")?.unwrap(), 2.0); 445 | assert_eq!(r.get("的情?下解.*?况")?.unwrap(), 2.0); 446 | assert!(r.get("的情下解.*?况")?.is_none()); 447 | 448 | Ok(()) 449 | } 450 | 451 | #[test] 452 | fn test_emoji() -> Result<()> { 453 | let input = r#"{"utf8":"Example emoji, KO: \ud83d\udd13, \ud83c\udfc3 OK: \u2764\ufe0f "}"#; 454 | let r = parse(input)?.unwrap(); 455 | assert_eq!(r.get("utf8")?.unwrap(), "Example emoji, KO: 🔓, 🏃 OK: ❤️ "); 456 | Ok(()) 457 | } 458 | 459 | #[test] 460 | fn test_parse_any() -> Result<()> { 461 | assert_eq!(parse("100")?.unwrap(), 100 as f64); 462 | assert_eq!(parse("true")?.unwrap(), true); 463 | assert_eq!(parse("false")?.unwrap(), false); 464 | assert_eq!(parse("yikes")?.is_some(), false); 465 | Ok(()) 466 | } 467 | 468 | #[test] 469 | fn test_map() -> Result<()> { 470 | let a = r#""asdf""#; 471 | let b = r#"{"asdf":"ghjk""#; 472 | let c = String::from(r#"**invalid**"#); 473 | let d = String::from(r#"{"#); 474 | assert!(parse(a)?.unwrap().as_object().is_none()); 475 | assert_eq!(parse(b)?.unwrap().as_object().unwrap()["asdf"], "ghjk"); 476 | assert!(Value::Object(std::borrow::Cow::Owned(c)) 477 | .as_object() 478 | .is_none()); 479 | assert!(Value::Object(std::borrow::Cow::Owned(d)) 480 | .as_object() 481 | .is_some()); 482 | Ok(()) 483 | } 484 | 485 | #[test] 486 | fn test_array() -> Result<()> { 487 | let json = r#" 488 | { 489 | "widget": { 490 | "debug": "on", 491 | "window": { 492 | "title": "Sample Konfabulator Widget", 493 | "name": "main_window", 494 | "width": 500, 495 | "height": 500 496 | }, 497 | "image": { 498 | "src": "Images/Sun.png", 499 | "hOffset": 250, 500 | "vOffset": 250, 501 | "alignment": "center" 502 | }, 503 | "text": { 504 | "data": "Click Here", 505 | "size": 36, 506 | "style": "bold", 507 | "vOffset": 100, 508 | "alignment": "center", 509 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 510 | }, 511 | "menu": [ 512 | { 513 | "title": "file", 514 | "sub_item": 7, 515 | "options": [1, 2, 3] 516 | }, 517 | { 518 | "title": "edit", 519 | "sub_item": 14, 520 | "options": [4, 5] 521 | }, 522 | { 523 | "title": "help", 524 | "sub_item": 4, 525 | "options": [6, 7, 8] 526 | } 527 | ] 528 | } 529 | }"#; 530 | let r = parse(json)?.unwrap(); 531 | let a = r.get("widget.menu.#(sub_item>5)#.title")?.unwrap(); 532 | assert_eq!(a.as_vec().unwrap(), vec!["file", "edit"]); 533 | 534 | let a = r.get("widget.menu.#.options.#(>4)")?.unwrap(); 535 | assert_eq!(a.as_vec().unwrap(), vec![5, 6]); 536 | 537 | let a = r.get("widget.menu.#.options.#(>4)#")?.unwrap(); 538 | assert_eq!(a.as_vec().unwrap().len(), 3); 539 | 540 | Ok(()) 541 | } 542 | 543 | #[test] 544 | fn test_issue_38() -> Result<()> { 545 | assert_eq!( 546 | parse(r#"["S3O PEDRO DO BUTI\udf93"]"#)? 547 | .unwrap() 548 | .get("0")? 549 | .unwrap(), 550 | r#"S3O PEDRO DO BUTI\udf93"# 551 | ); 552 | assert_eq!( 553 | parse(r#"["S3O PEDRO DO BUTI\udf93asdf"]"#)? 554 | .unwrap() 555 | .get("0")? 556 | .unwrap(), 557 | "S3O PEDRO DO BUTI\\udf93asdf" 558 | ); 559 | assert_eq!( 560 | parse(r#"["S3O PEDRO DO BUTI\udf93\u"]"#)? 561 | .unwrap() 562 | .get("0")? 563 | .unwrap(), 564 | "S3O PEDRO DO BUTI\\udf93\\u" 565 | ); 566 | assert_eq!( 567 | parse(r#"["S3O PEDRO DO BUTI\udf93\u1"]"#)? 568 | .unwrap() 569 | .get("0")? 570 | .unwrap(), 571 | "S3O PEDRO DO BUTI\\udf93\\u1" 572 | ); 573 | assert_eq!( 574 | parse(r#"["S3O PEDRO DO BUTI\udf93\u13"]"#)? 575 | .unwrap() 576 | .get("0")? 577 | .unwrap(), 578 | "S3O PEDRO DO BUTI\\udf93\\u13" 579 | ); 580 | assert_eq!( 581 | parse(r#"["S3O PEDRO DO BUTI\udf93\u134"]"#)? 582 | .unwrap() 583 | .get("0")? 584 | .unwrap(), 585 | "S3O PEDRO DO BUTI\\udf93\\u134" 586 | ); 587 | assert_eq!( 588 | parse(r#"["S3O PEDRO DO BUTI\udf93\u1345"]"#)? 589 | .unwrap() 590 | .get("0")? 591 | .unwrap(), 592 | "S3O PEDRO DO BUTI\\udf93ፅ" 593 | ); 594 | assert_eq!( 595 | parse(r#"["S3O PEDRO DO BUTI\udf93\u1345asd"]"#)? 596 | .unwrap() 597 | .get("0")? 598 | .unwrap(), 599 | "S3O PEDRO DO BUTI\\udf93ፅasd" 600 | ); 601 | 602 | Ok(()) 603 | } 604 | 605 | #[test] 606 | fn test_escape_path() -> Result<()> { 607 | let json = r#"{ 608 | "test":{ 609 | "*":"valZ", 610 | "*v":"val0", 611 | "keyv*":"val1", 612 | "key*v":"val2", 613 | "keyv?":"val3", 614 | "key?v":"val4", 615 | "keyv.":"val5", 616 | "key.v":"val6", 617 | "keyk*":{"key?":"val7"} 618 | } 619 | }"#; 620 | 621 | let r = parse(json)?.unwrap(); 622 | assert_eq!(r.get("test.\\*")?.unwrap(), "valZ"); 623 | assert_eq!(r.get("test.\\*v")?.unwrap(), "val0"); 624 | assert_eq!(r.get("test.keyv\\*")?.unwrap(), "val1"); 625 | assert_eq!(r.get("test.key\\*v")?.unwrap(), "val2"); 626 | assert_eq!(r.get("test.keyv\\?")?.unwrap(), "val3"); 627 | assert_eq!(r.get("test.key\\?v")?.unwrap(), "val4"); 628 | assert_eq!(r.get("test.keyv\\.")?.unwrap(), "val5"); 629 | assert_eq!(r.get("test.key\\.v")?.unwrap(), "val6"); 630 | assert_eq!(r.get("test.keyk\\*.key\\?")?.unwrap(), "val7"); 631 | 632 | Ok(()) 633 | } 634 | 635 | #[test] 636 | fn test_null_array() -> Result<()> { 637 | assert!(get(r#"{"data":null}"#, "data")?.unwrap().as_vec().is_none()); 638 | assert!(get(r#"{}"#, "data")?.is_none()); 639 | assert_eq!( 640 | parse(r#"{"data":[]}"#)? 641 | .unwrap() 642 | .get("data")? 643 | .unwrap() 644 | .as_vec() 645 | .unwrap() 646 | .len(), 647 | 0 648 | ); 649 | assert_eq!( 650 | parse(r#"{"data":[null]}"#)? 651 | .unwrap() 652 | .get("data")? 653 | .unwrap() 654 | .as_vec() 655 | .unwrap() 656 | .len(), 657 | 1 658 | ); 659 | 660 | Ok(()) 661 | } 662 | 663 | #[test] 664 | fn test_token_raw_for_literal() -> Result<()> { 665 | assert_eq!(parse("null")?.unwrap(), Value::Null); 666 | assert_eq!(parse("true")?.unwrap(), Value::Boolean(true)); 667 | assert_eq!(parse("false")?.unwrap(), Value::Boolean(false)); 668 | 669 | Ok(()) 670 | } 671 | 672 | #[test] 673 | fn test_single_array_value() -> Result<()> { 674 | let json = r#"{"key": "value","key2":[1,2,3,4,"A"]}"#; 675 | let array: Vec = get(&json, "key")?.into_iter().collect(); 676 | 677 | assert_eq!(array.len(), 1); 678 | assert_eq!(array[0], "value"); 679 | 680 | let array: Vec = get(&json, "key2.#")?.into_iter().collect(); 681 | assert_eq!(array.len(), 1); 682 | 683 | let r = get(&json, "key3")?; 684 | assert!(r.is_none()); 685 | 686 | Ok(()) 687 | } 688 | 689 | // #[test] 690 | // fn test_invalid_path() -> Result<()> { 691 | // let r = parse(BASIC_JSON)?.unwrap(); 692 | // assert!(r.get("loggy.programmers.#(firstName==").is_err()); 693 | // assert!(r.get("loggy.programmers.#(").is_err()); 694 | // assert!(r.get("loggy.programmers.#(firstName").is_err()); 695 | // assert!(r.get("loggy.programmers.#(first").is_err()); 696 | // assert!(r.get(r#"loggy.programmers.#(firstName=="x""#).is_err()); 697 | // assert!(r.get(r#"loggy.programmers.#()"#).is_err()); 698 | 699 | // Ok(()) 700 | // } 701 | 702 | #[test] 703 | fn test_bracket_in_array() -> Result<()> { 704 | let json = r##"{ 705 | "children": ["Sara","Alex]","Jack"], 706 | "##; 707 | let r = parse(json)?.unwrap(); 708 | assert_eq!(r.get("children.#")?.unwrap(), 3); 709 | assert_eq!( 710 | r.get("children")?.unwrap().as_vec().unwrap(), 711 | vec!["Sara", "Alex]", "Jack"] 712 | ); 713 | assert_eq!(r.get("children.1")?.unwrap(), "Alex]"); 714 | assert_eq!(r.get("child*.2")?.unwrap(), "Jack"); 715 | assert_eq!(r.get("c?ildren.0")?.unwrap(), "Sara"); 716 | 717 | Ok(()) 718 | } 719 | 720 | #[test] 721 | fn test_key_unicode() -> Result<()> { 722 | { 723 | let json = r#"{"sample_unicode\u0041\u0042": "HelloWorld"}"#; 724 | assert_eq!(get(json, "sample_unicodeAB")?.unwrap(), "HelloWorld"); 725 | } 726 | 727 | { 728 | let json = r#"{"的情况下解\u0030\u0031\u0032":{"key":1,"的情况":2}}"#; 729 | assert_eq!(get(json, "的情况下解012.key")?.unwrap(), 1); 730 | assert_eq!(get(json, "的情况下解012.的情况")?.unwrap(), 2); 731 | } 732 | 733 | Ok(()) 734 | } 735 | --------------------------------------------------------------------------------