use serde::de::{self, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
use serde_json::{Map, Number, Value};
use std::fmt;
pub(crate) const DUPLICATE_KEY_CODE: &str = "DUPLICATE_JSON_KEY";
struct Strict;
impl<'de> DeserializeSeed<'de> for Strict {
type Value = Value;
fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Value, D::Error> {
deserializer.deserialize_any(StrictVisitor)
}
}
struct StrictVisitor;
impl<'de> Visitor<'de> for StrictVisitor {
type Value = Value;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("any valid JSON value")
}
fn visit_bool<E>(self, v: bool) -> Result<Value, E> {
Ok(Value::Bool(v))
}
fn visit_i64<E>(self, v: i64) -> Result<Value, E> {
Ok(Value::Number(v.into()))
}
fn visit_u64<E>(self, v: u64) -> Result<Value, E> {
Ok(Value::Number(v.into()))
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<Value, E> {
Number::from_f64(v)
.map(Value::Number)
.ok_or_else(|| E::custom("number is not finite"))
}
fn visit_str<E>(self, v: &str) -> Result<Value, E> {
Ok(Value::String(v.to_owned()))
}
fn visit_string<E>(self, v: String) -> Result<Value, E> {
Ok(Value::String(v))
}
fn visit_none<E>(self) -> Result<Value, E> {
Ok(Value::Null)
}
fn visit_unit<E>(self) -> Result<Value, E> {
Ok(Value::Null)
}
fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Value, D::Error> {
Strict.deserialize(deserializer)
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Value, A::Error> {
let mut items = Vec::new();
while let Some(item) = seq.next_element_seed(Strict)? {
items.push(item);
}
Ok(Value::Array(items))
}
fn visit_map<A: MapAccess<'de>>(self, mut access: A) -> Result<Value, A::Error> {
let mut map = Map::new();
while let Some(key) = access.next_key::<String>()? {
if map.contains_key(&key) {
return Err(de::Error::custom(format!(
"{DUPLICATE_KEY_CODE}: object key {key:?} appears more than once"
)));
}
let value = access.next_value_seed(Strict)?;
map.insert(key, value);
}
Ok(Value::Object(map))
}
}
pub(crate) fn parse(raw: &str) -> Result<Value, serde_json::Error> {
let mut deserializer = serde_json::Deserializer::from_str(raw);
let value = Strict.deserialize(&mut deserializer)?;
deserializer.end()?;
Ok(value)
}
pub(crate) fn parse_labeled(raw: &str, label: &str) -> Result<Value, String> {
match parse(raw) {
Ok(value) => Ok(value),
Err(error) if is_duplicate_key(&error) => Err(format!(
"{DUPLICATE_KEY_CODE}: {label} repeats a JSON object key"
)),
Err(error) => Err(parse_error_message(label, &error)),
}
}
pub(crate) const NON_EXACT_NUMBER_CODE: &str = "NON_EXACT_NUMBER";
pub(crate) fn find_inexact_number(value: &Value) -> Option<String> {
fn walk(value: &Value, path: &mut String) -> bool {
match value {
Value::Number(number) => !(number.is_i64() || number.is_u64()),
Value::Array(items) => items.iter().enumerate().any(|(index, item)| {
let len = path.len();
path.push_str(&format!("[{index}]"));
let found = walk(item, path);
if !found {
path.truncate(len);
}
found
}),
Value::Object(map) => map.iter().any(|(key, item)| {
let len = path.len();
let key: String = key.chars().take(64).collect();
path.push_str(&format!(".{key}"));
let found = walk(item, path);
if !found {
path.truncate(len);
}
found
}),
_ => false,
}
}
let mut path = String::from("$");
walk(value, &mut path).then_some(path)
}
pub(crate) fn reject_inexact_numbers(value: &Value, label: &str) -> Result<(), String> {
match find_inexact_number(value) {
Some(path) => Err(format!(
"{NON_EXACT_NUMBER_CODE}: {label} holds a number at {path} that is not an integer in \
the i64/u64 range; send decimals and larger values as strings"
)),
None => Ok(()),
}
}
pub(crate) fn parse_error_message(label: &str, error: &serde_json::Error) -> String {
if error.to_string().contains("number out of range") {
format!(
"{NON_EXACT_NUMBER_CODE}: {label} holds a number that is not an integer in the \
i64/u64 range ({error}); send decimals and larger values as strings"
)
} else {
format!("{label} must be valid JSON: {error}")
}
}
pub(crate) fn is_duplicate_key(error: &serde_json::Error) -> bool {
error.to_string().contains(DUPLICATE_KEY_CODE)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_like_serde_json_when_keys_are_unique() {
let raw = r#"{"a":[1,-2,3.5,"x",null,true],"b":{"c":{}}}"#;
assert_eq!(
parse(raw).unwrap(),
serde_json::from_str::<Value>(raw).unwrap()
);
}
#[test]
fn rejects_repeated_keys_at_any_depth() {
for raw in [
r#"{"amount":"9999.00","amount":"25.00"}"#,
r#"{"a":{"b":1,"b":2}}"#,
r#"{"a":[{"k":1,"k":1}]}"#,
r#"{"a":1,"\u0061":2}"#,
] {
let error = parse(raw).unwrap_err();
assert!(is_duplicate_key(&error), "{raw}: {error}");
}
}
#[test]
fn finds_floats_and_out_of_range_integers_with_their_path() {
let ok = parse(r#"{"a":[1,-9223372036854775808,18446744073709551615],"s":"0.1"}"#).unwrap();
assert_eq!(find_inexact_number(&ok), None);
for (raw, path) in [
(r#"{"f":0.1}"#, "$.f"),
(
r#"{"a":[1,{"h":123456789012345678901234567890}]}"#,
"$.a[1].h",
),
(r#"[18446744073709551616]"#, "$[0]"),
(r#"{"k":-9223372036854775809}"#, "$.k"),
(r#"{"x":2.0}"#, "$.x"),
(r#"{"x":1e2}"#, "$.x"),
] {
let value = parse(raw).unwrap();
assert_eq!(find_inexact_number(&value).as_deref(), Some(path), "{raw}");
}
}
#[test]
fn same_key_in_sibling_objects_is_fine_and_trailing_data_is_rejected() {
assert!(parse(r#"[{"k":1},{"k":2}]"#).is_ok());
assert!(parse(r#"{"k":1} {"k":2}"#).is_err());
assert!(!is_duplicate_key(&parse("{").unwrap_err()));
}
}