Skip to main content

json_tools_rs/
lib.rs

1//! # JSON Tools RS
2//!
3//! A Rust library for advanced JSON manipulation, including flattening and unflattening
4//! nested JSON structures with configurable filtering and replacement options.
5//!
6//! ## Features
7//!
8//! - **Unified API**: Single `JSONTools` entry point for both flattening and unflattening
9//! - **Builder Pattern**: Fluent, chainable API for easy configuration
10//! - **Advanced Filtering**: Remove empty values (strings, objects, arrays, null values)
11//! - **Pattern Replacements**: Support for literal and regex-based key/value replacements
12//! - **High Performance**: SIMD-accelerated JSON parsing with optimized algorithms
13//! - **Batch Processing**: Handle single JSON strings or arrays of JSON strings
14//! - **Comprehensive Error Handling**: Detailed error messages for debugging
15//!
16//! ## Notes
17//!
18//! - **Root-level empty arrays**: Flattening an empty array (`[]`) produces `"{}"` (an empty
19//!   object), because flattening always yields key-value pairs. Zero elements means zero
20//!   key-value pairs, which is represented as an empty object.
21//!
22//! ## Quick Start with Unified API
23//!
24//! ### Flattening JSON
25//!
26//! ```rust
27//! use json_tools_rs::{JSONTools, JsonOutput};
28//!
29//! let json = r#"{"user": {"name": "John", "details": {"age": null, "city": ""}}}"#;
30//! let result = JSONTools::new()
31//!     .flatten()
32//!     .separator("::")
33//!     .lowercase_keys(true)
34//!     .key_replacement("r'(User|Admin)_'", "")
35//!     .value_replacement("@example.com", "@company.org")
36//!     .remove_empty_strings(true)
37//!     .remove_nulls(true)
38//!     .remove_empty_objects(true)
39//!     .remove_empty_arrays(true)
40//!     .execute(json).unwrap();
41//!
42//! match result {
43//!     JsonOutput::Single(flattened) => {
44//!         // Result: {"user::name": "John"}
45//!         println!("{}", flattened);
46//!     }
47//!     JsonOutput::Multiple(_) => unreachable!(),
48//! }
49//! ```
50//!
51//! ### Unflattening JSON
52//!
53//! ```rust
54//! use json_tools_rs::{JSONTools, JsonOutput};
55//!
56//! let flattened = r#"{"user::name": "John", "user::age": 30}"#;
57//! let result = JSONTools::new()
58//!     .unflatten()
59//!     .separator("::")
60//!     .lowercase_keys(true)
61//!     .key_replacement("r'(User|Admin)_'", "")
62//!     .value_replacement("@company.org", "@example.com")
63//!     .remove_empty_strings(true)
64//!     .remove_nulls(true)
65//!     .remove_empty_objects(true)
66//!     .remove_empty_arrays(true)
67//!     .execute(flattened).unwrap();
68//!
69//! match result {
70//!     JsonOutput::Single(unflattened) => {
71//!         // Result: {"user": {"name": "John", "age": 30}}
72//!         println!("{}", unflattened);
73//!     }
74//!     JsonOutput::Multiple(_) => unreachable!(),
75//! }
76//! ```
77//!
78//! # Doctests
79//!
80//! The following doctests demonstrate individual features in a progressive learning format.
81//! Each example focuses on a specific capability to help users understand how to use the library effectively.
82//!
83//! ## 1. Basic Flattening and Unflattening Operations
84//!
85//! ```rust
86//! use json_tools_rs::{JSONTools, JsonOutput};
87//!
88//! // Basic flattening - converts nested JSON to flat key-value pairs
89//! let nested_json = r#"{"user": {"name": "John", "profile": {"age": 30}}}"#;
90//! let result = JSONTools::new()
91//!     .flatten()
92//!     .execute(nested_json).unwrap();
93//!
94//! match result {
95//!     JsonOutput::Single(flattened) => {
96//!         // Result: {"user.name": "John", "user.profile.age": 30}
97//!         assert!(flattened.contains("user.name"));
98//!         assert!(flattened.contains("user.profile.age"));
99//!     }
100//!     JsonOutput::Multiple(_) => unreachable!(),
101//! }
102//!
103//! // Basic unflattening - converts flat JSON back to nested structure
104//! let flat_json = r#"{"user.name": "John", "user.profile.age": 30}"#;
105//! let result = JSONTools::new()
106//!     .unflatten()
107//!     .execute(flat_json).unwrap();
108//!
109//! match result {
110//!     JsonOutput::Single(unflattened) => {
111//!         // Result: {"user": {"name": "John", "profile": {"age": 30}}}
112//!         assert!(unflattened.contains(r#""user""#));
113//!         assert!(unflattened.contains(r#""name":"John""#));
114//!     }
115//!     JsonOutput::Multiple(_) => unreachable!(),
116//! }
117//! ```
118//!
119//! ## 2. Custom Separator Usage
120//!
121//! ```rust
122//! use json_tools_rs::{JSONTools, JsonOutput};
123//!
124//! // Using custom separator instead of default "."
125//! let json = r#"{"company": {"department": {"team": "engineering"}}}"#;
126//! let result = JSONTools::new()
127//!     .flatten()
128//!     .separator("::") // Use "::" instead of "."
129//!     .execute(json).unwrap();
130//!
131//! match result {
132//!     JsonOutput::Single(flattened) => {
133//!         // Result: {"company::department::team": "engineering"}
134//!         assert!(flattened.contains("company::department::team"));
135//!         assert!(!flattened.contains("company.department.team"));
136//!     }
137//!     JsonOutput::Multiple(_) => unreachable!(),
138//! }
139//! ```
140//!
141//! ## 3. Key Transformations - Lowercase Keys
142//!
143//! ```rust
144//! use json_tools_rs::{JSONTools, JsonOutput};
145//!
146//! // Convert all keys to lowercase during processing
147//! let json = r#"{"UserName": "John", "UserProfile": {"FirstName": "John"}}"#;
148//! let result = JSONTools::new()
149//!     .flatten()
150//!     .lowercase_keys(true)
151//!     .execute(json).unwrap();
152//!
153//! match result {
154//!     JsonOutput::Single(flattened) => {
155//!         // Result: {"username": "John", "userprofile.firstname": "John"}
156//!         assert!(flattened.contains("username"));
157//!         assert!(flattened.contains("userprofile.firstname"));
158//!         assert!(!flattened.contains("UserName"));
159//!     }
160//!     JsonOutput::Multiple(_) => unreachable!(),
161//! }
162//! ```
163//!
164//! ## 4. Key Replacement Patterns - Literal Replacement
165//!
166//! ```rust
167//! use json_tools_rs::{JSONTools, JsonOutput};
168//!
169//! // Replace literal strings in keys
170//! let json = r#"{"user_profile_name": "John", "user_profile_age": 30}"#;
171//! let result = JSONTools::new()
172//!     .flatten()
173//!     .key_replacement("user_profile_", "person_") // Replace literal string
174//!     .execute(json).unwrap();
175//!
176//! match result {
177//!     JsonOutput::Single(flattened) => {
178//!         // Result: {"person_name": "John", "person_age": 30}
179//!         assert!(flattened.contains("person_name"));
180//!         assert!(flattened.contains("person_age"));
181//!         assert!(!flattened.contains("user_profile_"));
182//!     }
183//!     JsonOutput::Multiple(_) => unreachable!(),
184//! }
185//! ```
186//!
187//! ## 5. Key Replacement Patterns - Regex Replacement
188//!
189//! ```rust
190//! use json_tools_rs::{JSONTools, JsonOutput};
191//!
192//! // Replace using regex patterns in keys
193//! let json = r#"{"user_name": "John", "admin_name": "Jane", "guest_name": "Bob"}"#;
194//! let result = JSONTools::new()
195//!     .flatten()
196//!     .key_replacement("r'(user|admin)_'", "person_") // Regex pattern
197//!     .execute(json).unwrap();
198//!
199//! match result {
200//!     JsonOutput::Single(flattened) => {
201//!         // Result: {"person_name": "John", "person_name": "Jane", "guest_name": "Bob"}
202//!         // Note: This would cause collision without collision handling
203//!         assert!(flattened.contains("person_name"));
204//!         assert!(flattened.contains("guest_name"));
205//!     }
206//!     JsonOutput::Multiple(_) => unreachable!(),
207//! }
208//! ```
209//!
210//! ## 6. Value Replacement Patterns - Literal Replacement
211//!
212//! ```rust
213//! use json_tools_rs::{JSONTools, JsonOutput};
214//!
215//! // Replace literal strings in values
216//! let json = r#"{"email": "user@example.com", "backup_email": "admin@example.com"}"#;
217//! let result = JSONTools::new()
218//!     .flatten()
219//!     .value_replacement("@example.com", "@company.org") // Replace domain
220//!     .execute(json).unwrap();
221//!
222//! match result {
223//!     JsonOutput::Single(flattened) => {
224//!         // Result: {"email": "user@company.org", "backup_email": "admin@company.org"}
225//!         assert!(flattened.contains("@company.org"));
226//!         assert!(!flattened.contains("@example.com"));
227//!     }
228//!     JsonOutput::Multiple(_) => unreachable!(),
229//! }
230//! ```
231//!
232//! ## 7. Value Replacement Patterns - Regex Replacement
233//!
234//! ```rust
235//! use json_tools_rs::{JSONTools, JsonOutput};
236//!
237//! // Replace using regex patterns in values
238//! let json = r#"{"role": "super", "level": "admin", "type": "user"}"#;
239//! let result = JSONTools::new()
240//!     .flatten()
241//!     .value_replacement("r'^(super|admin)$'", "administrator") // Regex pattern
242//!     .execute(json).unwrap();
243//!
244//! match result {
245//!     JsonOutput::Single(flattened) => {
246//!         // Result: {"role": "administrator", "level": "administrator", "type": "user"}
247//!         assert!(flattened.contains(r#""role":"administrator""#));
248//!         assert!(flattened.contains(r#""level":"administrator""#));
249//!         assert!(flattened.contains(r#""type":"user""#));
250//!     }
251//!     JsonOutput::Multiple(_) => unreachable!(),
252//! }
253//! ```
254//!
255//! ## 8. Filtering Options - Remove Empty Strings
256//!
257//! ```rust
258//! use json_tools_rs::{JSONTools, JsonOutput};
259//!
260//! // Remove keys that have empty string values
261//! let json = r#"{"name": "John", "nickname": "", "age": 30}"#;
262//! let result = JSONTools::new()
263//!     .flatten()
264//!     .remove_empty_strings(true)
265//!     .execute(json).unwrap();
266//!
267//! match result {
268//!     JsonOutput::Single(flattened) => {
269//!         // Result: {"name": "John", "age": 30} - "nickname" removed
270//!         assert!(flattened.contains("name"));
271//!         assert!(flattened.contains("age"));
272//!         assert!(!flattened.contains("nickname"));
273//!     }
274//!     JsonOutput::Multiple(_) => unreachable!(),
275//! }
276//! ```
277//!
278//! ## 9. Filtering Options - Remove Null Values
279//!
280//! ```rust
281//! use json_tools_rs::{JSONTools, JsonOutput};
282//!
283//! // Remove keys that have null values
284//! let json = r#"{"name": "John", "age": null, "city": "NYC"}"#;
285//! let result = JSONTools::new()
286//!     .flatten()
287//!     .remove_nulls(true)
288//!     .execute(json).unwrap();
289//!
290//! match result {
291//!     JsonOutput::Single(flattened) => {
292//!         // Result: {"name": "John", "city": "NYC"} - "age" removed
293//!         assert!(flattened.contains("name"));
294//!         assert!(flattened.contains("city"));
295//!         assert!(!flattened.contains("age"));
296//!     }
297//!     JsonOutput::Multiple(_) => unreachable!(),
298//! }
299//! ```
300//!
301//! ## 10. Filtering Options - Remove Empty Objects and Arrays
302//!
303//! ```rust
304//! use json_tools_rs::{JSONTools, JsonOutput};
305//!
306//! // Remove keys that have empty objects or arrays
307//! let json = r#"{"user": {"name": "John"}, "tags": [], "metadata": {}}"#;
308//! let result = JSONTools::new()
309//!     .flatten()
310//!     .remove_empty_objects(true)
311//!     .remove_empty_arrays(true)
312//!     .execute(json).unwrap();
313//!
314//! match result {
315//!     JsonOutput::Single(flattened) => {
316//!         // Result: {"user.name": "John"} - "tags" and "metadata" removed
317//!         assert!(flattened.contains("user.name"));
318//!         assert!(!flattened.contains("tags"));
319//!         assert!(!flattened.contains("metadata"));
320//!     }
321//!     JsonOutput::Multiple(_) => unreachable!(),
322//! }
323//! ```
324//!
325//!
326//! ## 11. Collision Handling - Collect Values into Arrays
327//!
328//! ```rust
329//! use json_tools_rs::{JSONTools, JsonOutput};
330//!
331//! // When key replacements cause collisions, collect all values into an array
332//! let json = r#"{"user_name": "John", "admin_name": "Jane"}"#;
333//! let result = JSONTools::new()
334//!     .flatten()
335//!     .key_replacement("r'(user|admin)_'", "") // This creates collision: both become "name"
336//!     .handle_key_collision(true) // Collect values into array
337//!     .execute(json).unwrap();
338//!
339//! match result {
340//!     JsonOutput::Single(flattened) => {
341//!         // Result: {"name": ["John", "Jane"]}
342//!         assert!(flattened.contains(r#""name":["John","Jane"]"#) ||
343//!                 flattened.contains(r#""name": ["John", "Jane"]"#));
344//!     }
345//!     JsonOutput::Multiple(_) => unreachable!(),
346//! }
347//! ```
348//!
349//! ## 12. Comprehensive Integration Example
350//!
351//! ```rust
352//! use json_tools_rs::{JSONTools, JsonOutput};
353//!
354//! // Comprehensive example combining multiple features for real-world usage
355//! let complex_json = r#"{
356//!     "User_Profile": {
357//!         "Personal_Info": {
358//!             "FirstName": "John",
359//!             "LastName": "",
360//!             "Email": "john@example.com",
361//!             "Age": null
362//!         },
363//!         "Settings": {
364//!             "Theme": "dark",
365//!             "Notifications": {},
366//!             "Tags": []
367//!         }
368//!     },
369//!     "Admin_Profile": {
370//!         "Personal_Info": {
371//!             "FirstName": "Jane",
372//!             "Email": "jane@example.com",
373//!             "Role": "super"
374//!         }
375//!     }
376//! }"#;
377//!
378//! let result = JSONTools::new()
379//!     .flatten()
380//!     .separator("::") // Use custom separator
381//!     .lowercase_keys(true) // Convert all keys to lowercase
382//!     .key_replacement("r'(user|admin)_profile::'", "person::") // Normalize profile keys
383//!     .key_replacement("personal_info::", "info::") // Simplify nested keys
384//!     .value_replacement("@example.com", "@company.org") // Update email domain
385//!     .value_replacement("r'^super$'", "administrator") // Normalize role values
386//!     .remove_empty_strings(true) // Remove empty string values
387//!     .remove_nulls(true) // Remove null values
388//!     .remove_empty_objects(true) // Remove empty objects
389//!     .remove_empty_arrays(true) // Remove empty arrays
390//!     .handle_key_collision(true) // Handle any key collisions by collecting into arrays
391//!     .execute(complex_json).unwrap();
392//!
393//! match result {
394//!     JsonOutput::Single(flattened) => {
395//!         // Verify the comprehensive transformation worked
396//!         // Note: Keys are transformed through multiple steps: lowercase + replacements
397//!         assert!(flattened.contains("@company.org"));
398//!         assert!(flattened.contains("administrator"));
399//!         assert!(!flattened.contains("lastname")); // Empty string removed
400//!         assert!(!flattened.contains("age")); // Null removed
401//!         assert!(!flattened.contains("notifications")); // Empty object removed
402//!         assert!(!flattened.contains("tags")); // Empty array removed
403//!         // The exact key structure depends on the order of transformations
404//!         println!("Comprehensive transformation result: {}", flattened);
405//!     }
406//!     JsonOutput::Multiple(_) => unreachable!(),
407//! }
408//!
409//! // Demonstrate unflattening with the same configuration
410//! let flat_json = r#"{"person::info::name": "Alice", "person::settings::theme": "light"}"#;
411//! let result = JSONTools::new()
412//!     .unflatten()
413//!     .separator("::")
414//!     .execute(flat_json).unwrap();
415//!
416//! match result {
417//!     JsonOutput::Single(unflattened) => {
418//!         // Result: {"person": {"info": {"name": "Alice"}, "settings": {"theme": "light"}}}
419//!         assert!(unflattened.contains(r#""person""#));
420//!         assert!(unflattened.contains(r#""info""#));
421//!         assert!(unflattened.contains(r#""settings""#));
422//!         println!("Unflattening result: {}", unflattened);
423//!     }
424//!     JsonOutput::Multiple(_) => unreachable!(),
425//! }
426//! ```
427//!
428
429// Use mimalloc for ~5-10% performance improvement on allocation-heavy workloads.
430// Only enabled when the mimalloc feature is active (default for Rust, excluded for Python).
431#[cfg(feature = "mimalloc")]
432#[global_allocator]
433static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
434
435// ================================================================================================
436// MODULE DECLARATIONS
437// ================================================================================================
438
439mod builder;
440pub(crate) mod cache;
441mod config;
442pub(crate) mod convert;
443mod error;
444pub(crate) mod flatten;
445pub(crate) mod fxhash;
446pub(crate) mod json_parser;
447pub(crate) mod transform;
448mod types;
449pub(crate) mod unflatten;
450
451#[cfg(feature = "python")]
452mod python;
453
454#[cfg(feature = "jvm")]
455mod jvm;
456
457#[cfg(test)]
458mod tests;
459
460// ================================================================================================
461// PUBLIC RE-EXPORTS (preserves backward-compatible import paths)
462// ================================================================================================
463
464pub use builder::JSONTools;
465pub use config::{CollisionConfig, FilteringConfig, ProcessingConfig, ReplacementConfig};
466pub use error::JsonToolsError;
467pub use types::{JsonInput, JsonOutput};