use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum JsonValue {
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Array(Vec<JsonValue>),
Object(Vec<(String, JsonValue)>),
}
impl JsonValue {
pub fn to_json_string(&self) -> String {
self.to_json_string_with_options(true)
}
pub fn to_json_string_with_options(&self, ensure_ascii: bool) -> String {
match self {
JsonValue::Null => "null".to_string(),
JsonValue::Bool(b) => if *b { "true" } else { "false" }.to_string(),
JsonValue::Integer(n) => n.to_string(),
JsonValue::Float(f) => {
if f.fract() == 0.0 && f.abs() < 1e15 {
format!("{:.1}", f)
} else {
f.to_string()
}
}
JsonValue::String(s) => format!("\"{}\"", escape_json_string(s, ensure_ascii)),
JsonValue::Array(arr) => {
let items: Vec<String> = arr
.iter()
.map(|v| v.to_json_string_with_options(ensure_ascii))
.collect();
format!("[{}]", items.join(", "))
}
JsonValue::Object(obj) => {
let items: Vec<String> = obj
.iter()
.map(|(k, v)| {
format!(
"\"{}\": {}",
escape_json_string(k, ensure_ascii),
v.to_json_string_with_options(ensure_ascii)
)
})
.collect();
format!("{{{}}}", items.join(", "))
}
}
}
pub fn is_empty(&self) -> bool {
match self {
JsonValue::String(s) => s.is_empty(),
JsonValue::Array(arr) => arr.is_empty(),
JsonValue::Object(obj) => obj.is_empty(),
_ => false,
}
}
pub fn is_strictly_empty(&self) -> bool {
match self {
JsonValue::String(s) => s.is_empty(),
JsonValue::Array(arr) => arr.is_empty(),
JsonValue::Object(obj) => obj.is_empty(),
_ => false,
}
}
}
fn escape_json_string(s: &str, ensure_ascii: bool) -> String {
let mut result = String::with_capacity(s.len());
for c in s.chars() {
match c {
'"' => result.push_str("\\\""),
'\\' => result.push_str("\\\\"),
'\n' => result.push_str("\\n"),
'\r' => result.push_str("\\r"),
'\t' => result.push_str("\\t"),
'\x08' => result.push_str("\\b"),
'\x0c' => result.push_str("\\f"),
c if c.is_control() => {
result.push_str(&format!("\\u{:04x}", c as u32));
}
c if !c.is_ascii() && ensure_ascii => {
let code = c as u32;
if code <= 0xFFFF {
result.push_str(&format!("\\u{:04x}", code));
} else {
let code = code - 0x10000;
let high = 0xD800 + (code >> 10);
let low = 0xDC00 + (code & 0x3FF);
result.push_str(&format!("\\u{:04x}\\u{:04x}", high, low));
}
}
c => result.push(c),
}
}
result
}
impl From<serde_json::Value> for JsonValue {
fn from(value: serde_json::Value) -> Self {
match value {
serde_json::Value::Null => JsonValue::Null,
serde_json::Value::Bool(b) => JsonValue::Bool(b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
JsonValue::Integer(i)
} else if let Some(f) = n.as_f64() {
JsonValue::Float(f)
} else {
JsonValue::String(n.to_string())
}
}
serde_json::Value::String(s) => JsonValue::String(s),
serde_json::Value::Array(arr) => {
JsonValue::Array(arr.into_iter().map(JsonValue::from).collect())
}
serde_json::Value::Object(obj) => {
JsonValue::Object(
obj.into_iter()
.map(|(k, v)| {
let clean_key =
k.trim_end_matches(|c: char| c.is_whitespace()).to_string();
(clean_key, JsonValue::from(v))
})
.collect(),
)
}
}
}
}
impl fmt::Display for JsonValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_json_string())
}
}
impl Default for JsonValue {
fn default() -> Self {
JsonValue::Null
}
}