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};