use serde_json::Value; /// Your json MUST BE valid and generated by `serde_json::to_vec` before being /// sent in this function. This function is DUMB and FAST but makes a lot of /// asumption about the way `serde_json` will generate its input. /// /// Will return `true` if the JSON contains an object, an array of array /// or an array containing an object. Returns `false` for everything else. pub fn should_flatten_from_unchecked_slice(json: &[u8]) -> bool { if json.is_empty() { return false; } // since the json we receive has been generated by serde_json we know // it doesn't contains any whitespace at the beginning thus we can check // directly if we're looking at an object. if json[0] == b'{' { return true; } else if json[0] != b'[' { // if the json isn't an object or an array it means it's a simple value. return false; } // The array case is a little bit more complex. We are looking for a second // `[` but we need to ensure that it doesn't appear inside of a string. Thus // we need to keep track of if we're in a string or not. // will be used when we met a `\` to skip the next character. let mut skip_next = false; let mut in_string = false; for byte in json.iter().skip(1) { match byte { // handle the backlash. _ if skip_next => skip_next = false, b'\\' => skip_next = true, // handle the strings. byte if in_string => { if *byte == b'"' { in_string = false; } } b'"' => in_string = true, // handle the arrays. b'[' => return true, // since we know the json is valid we don't need to ensure the // array is correctly closed // handle the objects. b'{' => return true, // ignore everything else _ => (), } } false } /// Consider using [`should_flatten_from_unchecked_slice`] when you can. /// Will returns `true` if the json contains an object, an array of array /// or an array containing an object. /// Returns `false` for everything else. /// This function has been written to test the [`should_flatten_from_unchecked_slice`]. pub fn should_flatten_from_value(json: &Value) -> bool { match json { Value::Object(..) => true, Value::Array(array) => array.iter().any(|value| value.is_array() || value.is_object()), _ => false, } } #[cfg(test)] mod tests { use serde_json::*; use super::*; #[test] fn test_shouldnt_flatten() { let shouldnt_flatten = vec![ json!(null), json!(true), json!(false), json!("a superb string"), json!("a string escaping other \"string\""), json!([null, true, false]), json!(["hello", "world", "!"]), json!(["a \"string\" escaping 'an other'", "\"[\"", "\"{\""]), ]; for value in shouldnt_flatten { assert!(!should_flatten_from_value(&value)); let value = serde_json::to_vec(&value).unwrap(); assert!(!should_flatten_from_unchecked_slice(&value)); } } #[test] fn test_should_flatten() { let should_flatten = vec![ json!({}), json!({ "hello": "world" }), json!(["hello", ["world"]]), json!([true, true, true, true, true, true, true, true, true, {}]), ]; for value in should_flatten { assert!(should_flatten_from_value(&value)); let value = serde_json::to_vec(&value).unwrap(); assert!(should_flatten_from_unchecked_slice(&value)); } } }