| use crate::JsonValue; |
| use std::collections::HashMap; |
| use std::fmt; |
| use std::io::{self, Write}; |
| |
| /// Serialization error. This error only happens when some write error happens on writing the serialized byte sequence |
| /// to the given `io::Write` object. |
| #[derive(Debug)] |
| pub struct JsonGenerateError { |
| msg: String, |
| } |
| |
| impl JsonGenerateError { |
| pub fn message(&self) -> &str { |
| self.msg.as_str() |
| } |
| } |
| |
| impl fmt::Display for JsonGenerateError { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "Generate error: {}", &self.msg) |
| } |
| } |
| |
| impl std::error::Error for JsonGenerateError {} |
| |
| /// Convenient type alias for serialization results. |
| pub type JsonGenerateResult = Result<String, JsonGenerateError>; |
| |
| /// JSON serializer for `JsonValue`. |
| /// |
| /// Basically you don't need to use this struct directly since `JsonValue::stringify` or `JsonValue::format` methods are |
| /// using this internally. |
| /// |
| /// ``` |
| /// use tinyjson::{JsonGenerator, JsonValue}; |
| /// |
| /// let v = JsonValue::from("hello, world".to_string()); |
| /// let mut buf = vec![]; |
| /// let mut gen = JsonGenerator::new(&mut buf); |
| /// gen.generate(&v).unwrap(); |
| /// |
| /// assert_eq!(String::from_utf8(buf).unwrap(), "\"hello, world\""); |
| /// ``` |
| pub struct JsonGenerator<'indent, W: Write> { |
| out: W, |
| indent: Option<&'indent str>, |
| } |
| |
| impl<'indent, W: Write> JsonGenerator<'indent, W> { |
| /// Create a new `JsonGenerator` object. The serialized byte sequence will be written to the given `io::Write` |
| /// object. |
| pub fn new(out: W) -> Self { |
| Self { out, indent: None } |
| } |
| |
| /// Set indent string. This will be used by [`JsonGenerator::generate`]. |
| /// ``` |
| /// use tinyjson::{JsonGenerator, JsonValue}; |
| /// |
| /// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]); |
| /// let mut buf = vec![]; |
| /// let mut gen = JsonGenerator::new(&mut buf).indent(" "); |
| /// gen.generate(&v).unwrap(); |
| /// |
| /// assert_eq!(String::from_utf8(buf).unwrap(), |
| /// "[ |
| /// 1, |
| /// 2, |
| /// 3 |
| /// ]"); |
| /// ``` |
| pub fn indent(mut self, indent: &'indent str) -> Self { |
| self.indent = Some(indent); |
| self |
| } |
| |
| fn quote(&mut self, s: &str) -> io::Result<()> { |
| const B: u8 = b'b'; // \x08 |
| const T: u8 = b't'; // \x09 |
| const N: u8 = b'n'; // \x0a |
| const F: u8 = b'f'; // \x0c |
| const R: u8 = b'r'; // \x0d |
| const Q: u8 = b'"'; // \x22 |
| const S: u8 = b'\\'; // \x5c |
| const U: u8 = 1; // non-printable |
| |
| #[rustfmt::skip] |
| const ESCAPE_TABLE: [u8; 256] = [ |
| // 0 1 2 3 4 5 6 7 8 9 A B C D E F |
| U, U, U, U, U, U, U, U, B, T, N, U, F, R, U, U, // 0 |
| U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, // 1 |
| 0, 0, Q, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, S, 0, 0, 0, // 5 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F |
| ]; |
| |
| self.out.write_all(b"\"")?; |
| let mut start = 0; |
| for (i, c) in s.char_indices() { |
| let u = c as usize; |
| if u < 256 { |
| let esc = ESCAPE_TABLE[u]; |
| if esc == 0 { |
| continue; |
| } |
| if start != i { |
| self.out.write_all(s[start..i].as_bytes())?; |
| } |
| if esc == U { |
| write!(self.out, "\\u{:04x}", u)?; |
| } else { |
| self.out.write_all(&[b'\\', esc])?; |
| } |
| start = i + 1; |
| } |
| } |
| if start != s.len() { |
| self.out.write_all(s[start..].as_bytes())?; |
| } |
| self.out.write_all(b"\"") |
| } |
| |
| fn number(&mut self, f: f64) -> io::Result<()> { |
| if f.is_infinite() { |
| Err(io::Error::new( |
| io::ErrorKind::Other, |
| "JSON cannot represent inf", |
| )) |
| } else if f.is_nan() { |
| Err(io::Error::new( |
| io::ErrorKind::Other, |
| "JSON cannot represent NaN", |
| )) |
| } else { |
| write!(self.out, "{}", f) |
| } |
| } |
| |
| fn encode_array(&mut self, array: &[JsonValue]) -> io::Result<()> { |
| self.out.write_all(b"[")?; |
| let mut first = true; |
| for elem in array.iter() { |
| if first { |
| first = false; |
| } else { |
| self.out.write_all(b",")?; |
| } |
| self.encode(elem)?; |
| } |
| self.out.write_all(b"]") |
| } |
| |
| fn encode_object(&mut self, m: &HashMap<String, JsonValue>) -> io::Result<()> { |
| self.out.write_all(b"{")?; |
| let mut first = true; |
| for (k, v) in m { |
| if first { |
| first = false; |
| } else { |
| self.out.write_all(b",")?; |
| } |
| self.quote(k)?; |
| self.out.write_all(b":")?; |
| self.encode(v)?; |
| } |
| self.out.write_all(b"}") |
| } |
| |
| fn encode(&mut self, value: &JsonValue) -> io::Result<()> { |
| match value { |
| JsonValue::Number(n) => self.number(*n), |
| JsonValue::Boolean(b) => write!(self.out, "{}", *b), |
| JsonValue::String(s) => self.quote(s), |
| JsonValue::Null => self.out.write_all(b"null"), |
| JsonValue::Array(a) => self.encode_array(a), |
| JsonValue::Object(o) => self.encode_object(o), |
| } |
| } |
| |
| fn write_indent(&mut self, indent: &str, level: usize) -> io::Result<()> { |
| for _ in 0..level { |
| self.out.write_all(indent.as_bytes())?; |
| } |
| Ok(()) |
| } |
| |
| fn format_array(&mut self, array: &[JsonValue], indent: &str, level: usize) -> io::Result<()> { |
| if array.is_empty() { |
| return self.out.write_all(b"[]"); |
| } |
| |
| self.out.write_all(b"[\n")?; |
| let mut first = true; |
| for elem in array.iter() { |
| if first { |
| first = false; |
| } else { |
| self.out.write_all(b",\n")?; |
| } |
| self.write_indent(indent, level + 1)?; |
| self.format(elem, indent, level + 1)?; |
| } |
| self.out.write_all(b"\n")?; |
| self.write_indent(indent, level)?; |
| self.out.write_all(b"]") |
| } |
| |
| fn format_object( |
| &mut self, |
| m: &HashMap<String, JsonValue>, |
| indent: &str, |
| level: usize, |
| ) -> io::Result<()> { |
| if m.is_empty() { |
| return self.out.write_all(b"{}"); |
| } |
| |
| self.out.write_all(b"{\n")?; |
| let mut first = true; |
| for (k, v) in m { |
| if first { |
| first = false; |
| } else { |
| self.out.write_all(b",\n")?; |
| } |
| self.write_indent(indent, level + 1)?; |
| self.quote(k)?; |
| self.out.write_all(b": ")?; |
| self.format(v, indent, level + 1)?; |
| } |
| self.out.write_all(b"\n")?; |
| self.write_indent(indent, level)?; |
| self.out.write_all(b"}") |
| } |
| |
| fn format(&mut self, value: &JsonValue, indent: &str, level: usize) -> io::Result<()> { |
| match value { |
| JsonValue::Number(n) => self.number(*n), |
| JsonValue::Boolean(b) => write!(self.out, "{}", *b), |
| JsonValue::String(s) => self.quote(s), |
| JsonValue::Null => self.out.write_all(b"null"), |
| JsonValue::Array(a) => self.format_array(a, indent, level), |
| JsonValue::Object(o) => self.format_object(o, indent, level), |
| } |
| } |
| |
| /// Serialize the `JsonValue` into UTF-8 byte sequence. The result will be written to the `io::Write` object passed |
| /// at [`JsonGenerator::new`]. |
| /// This method serializes the value without indentation by default. But after setting an indent string via |
| /// [`JsonGenerator::indent`], this method will use the indent for elements of array and object. |
| /// |
| /// ``` |
| /// use tinyjson::{JsonGenerator, JsonValue}; |
| /// |
| /// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]); |
| /// |
| /// let mut buf = vec![]; |
| /// let mut gen = JsonGenerator::new(&mut buf); |
| /// gen.generate(&v).unwrap(); |
| /// assert_eq!(String::from_utf8(buf).unwrap(), "[1,2,3]"); |
| /// |
| /// let mut buf = vec![]; |
| /// let mut gen = JsonGenerator::new(&mut buf).indent(" "); // with 2-spaces indent |
| /// gen.generate(&v).unwrap(); |
| /// |
| /// assert_eq!(String::from_utf8(buf).unwrap(), |
| /// "[ |
| /// 1, |
| /// 2, |
| /// 3 |
| /// ]"); |
| /// ``` |
| pub fn generate(&mut self, value: &JsonValue) -> io::Result<()> { |
| if let Some(indent) = &self.indent { |
| self.format(value, indent, 0) |
| } else { |
| self.encode(value) |
| } |
| } |
| } |
| |
| /// Serialize the given `JsonValue` value to `String` without indentation. This method is almost identical to |
| /// `JsonValue::stringify` but exists for a historical reason. |
| /// |
| /// ``` |
| /// use tinyjson::JsonValue; |
| /// |
| /// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]); |
| /// let s = tinyjson::stringify(&v).unwrap(); |
| /// assert_eq!(s, "[1,2,3]"); |
| /// ``` |
| pub fn stringify(value: &JsonValue) -> JsonGenerateResult { |
| let mut to = Vec::new(); |
| let mut gen = JsonGenerator::new(&mut to); |
| gen.generate(value).map_err(|err| JsonGenerateError { |
| msg: format!("{}", err), |
| })?; |
| Ok(String::from_utf8(to).unwrap()) |
| } |
| |
| /// Serialize the given `JsonValue` value to `String` with 2-spaces indentation. This method is almost identical to |
| /// `JsonValue::format` but exists for a historical reason. |
| /// |
| /// ``` |
| /// use tinyjson::JsonValue; |
| /// |
| /// let v = JsonValue::from(vec![1.0.into(), 2.0.into(), 3.0.into()]); |
| /// let s = tinyjson::format(&v).unwrap(); |
| /// assert_eq!(s, "[\n 1,\n 2,\n 3\n]"); |
| /// ``` |
| pub fn format(value: &JsonValue) -> JsonGenerateResult { |
| let mut to = Vec::new(); |
| let mut gen = JsonGenerator::new(&mut to).indent(" "); |
| gen.generate(value).map_err(|err| JsonGenerateError { |
| msg: format!("{}", err), |
| })?; |
| Ok(String::from_utf8(to).unwrap()) |
| } |