Lines
99.8 %
Functions
65.9 %
Branches
100 %
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
use core::convert::Infallible;
use core::fmt::{self, Display};
use core::ops::{Deref, DerefMut, Index, IndexMut};
use crate::parser::{JsonKind, ParseConfig, ParseDelegate, Parser};
use crate::{Error, JsonNumber, JsonString};
/// A JSON value.
///
/// The `Backing` generic is the storage mechanism used by [`JsonNumber`] and
/// [`JsonString`]. This is generally `&str` or `Cow<str>`.
#[derive(Debug, Eq, PartialEq)]
pub enum Value<'a> {
/// A JSON number.
Number(JsonNumber<'a>),
/// A JSON string.
String(JsonString<'a>),
/// A boolean value.
Boolean(bool),
/// A JSON object (key/value pairs).
Object(Object<'a>),
/// A JSON array (list of values).
Array(Vec<Value<'a>>),
/// A null value.
Null,
}
impl<'a> Value<'a> {
/// Parses a JSON value from `json`, returning a `Value<&str>` that borrows
/// data from `json`.
/// Because the `str` type guarantees that `json` is valid UTF-8, no
/// additional unicode checks are performed on unescaped unicode sequences.
pub fn from_json(json: &'a str) -> Result<Self, Error> {
Self::from_json_with_config(json, ParseConfig::default())
/// Parses a JSON value from `json` using the settings from`config`,
/// returning a `Value<&str>` that borrows data from `json`.
pub fn from_json_with_config(json: &'a str, config: ParseConfig) -> Result<Self, Error> {
Parser::parse_json_with_config(json, config, ValueParser)
/// This function verifies that `json` is valid UTF-8 while parsing the
/// JSON.
pub fn from_json_bytes(json: &'a [u8]) -> Result<Self, Error> {
Self::from_json_bytes_with_config(json, ParseConfig::default())
pub fn from_json_bytes_with_config(json: &'a [u8], config: ParseConfig) -> Result<Self, Error> {
Parser::parse_json_bytes_with_config(json, config, ValueParser)
/// Returns the [`Object`] inside of this value, if this is a
/// [`Value::Object`].
#[must_use]
#[inline]
pub const fn as_object(&self) -> Option<&Object<'a>> {
if let Self::Object(obj) = self {
Some(obj)
} else {
None
/// Returns a mutable reference to the [`Object`] inside of this value, if
/// this is a [`Value::Object`].
pub fn as_object_mut(&mut self) -> Option<&mut Object<'a>> {
/// Returns the contained value associated with `key`, if this is a
/// [`Value::Object`]. Returns `None` if the value is not an object or if
/// the key is not found.
/// # Performance
/// [`Object`] uses a `Vec` of [`Entry`] types to store its entries. If the
/// operation being performed can be done with a single iteration over the
/// value's contents instead of multiple random accesses, the iteration
/// should be preferred. Additional options to make random access faster in
/// environments that can support it [are being considered][issue] for
/// future releases.
/// [issue]: https://github.com/khonsulabs/justjson/issues/7
pub fn get(&self, key: &str) -> Option<&Value<'a>> {
let object = self.as_object()?;
object.get(key)
/// Returns a mutable reference to the contained value associated with
/// `key`, if this is a [`Value::Object`]. Returns `None` if the value is
/// not an object or if the key is not found.
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value<'a>> {
let object = self.as_object_mut()?;
object.get_mut(key)
/// Returns the [`JsonString`] inside of this value, if this is a
/// [`Value::String`].
pub const fn as_string(&self) -> Option<&JsonString<'a>> {
if let Self::String(obj) = self {
/// Returns a reference to the contents of this value if it is a
/// [`Value::String`], and it does not have any escape sequences that need
/// to be decoded.
pub fn as_str(&self) -> Option<&str> {
if let Self::String(json_string) = self {
json_string.as_str()
/// Returns the [`JsonNumber`] inside of this value, if this is a
/// [`Value::Number`].
pub const fn as_number(&self) -> Option<&JsonNumber<'a>> {
if let Self::Number(obj) = self {
/// Parses the contained value as an [`f32`], if it is a number.
/// The JSON parser only validates that the number takes a correct form. If
/// a number cannot be parsed by the underlying routine due to having too
/// many digits, it this function can return None.
pub fn as_f32(&self) -> Option<f32> {
self.as_number().and_then(JsonNumber::as_f32)
/// Parses the contained value as an [`f64`], if it is a number.
pub fn as_f64(&self) -> Option<f64> {
self.as_number().and_then(JsonNumber::as_f64)
/// Returns the `bool` inside of this value, if this is a
/// [`Value::Boolean`].
pub const fn as_bool(&self) -> Option<bool> {
if let Self::Boolean(value) = self {
Some(*value)
/// Returns the slice of values inside of this value, if this is a
/// [`Value::Array`].
pub fn as_array(&self) -> Option<&[Self]> {
if let Self::Array(value) = self {
Some(value)
/// Returns a mutable reference to the Vec of values inside of this value,
/// if this is a [`Value::Array`].
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Self>> {
/// Returns the contained value at `index`, if this is a [`Value::Array`].
/// Returns `None` if the value is not an array or if `index` is beyond the
/// bounds of the array.
pub fn get_index(&self, index: usize) -> Option<&Value<'a>> {
let sequence = self.as_array()?;
sequence.get(index)
/// Returns a mutable reference to the contained value at `index`, if this
/// is a [`Value::Array`]. Returns `None` if the value is not an array or if
/// `index` is beyond the bounds of the array.
pub fn get_index_mut(&mut self, index: usize) -> Option<&mut Value<'a>> {
let sequence = self.as_array_mut()?;
sequence.get_mut(index)
/// Returns true if this value is `null`/[`Value::Null`].
pub const fn is_null(&self) -> bool {
matches!(self, Value::Null)
fn write_json<W: fmt::Write, const PRETTY: bool>(
&self,
indentation: &str,
line_ending: &str,
destination: W,
) -> fmt::Result {
let mut state = WriteState::<W, PRETTY>::new(destination, indentation, line_ending);
self.write_json_value(&mut state)
fn write_json_value<W: fmt::Write, const PRETTY: bool>(
state: &mut WriteState<'_, W, PRETTY>,
match self {
Value::String(string) => state.write_json(string),
Value::Number(number) => state.write(number.source()),
Value::Boolean(bool) => state.write(if *bool { "true" } else { "false" }),
Value::Null => state.write("null"),
Value::Object(obj) => Self::write_json_object(obj, state),
Value::Array(array) => Self::write_json_array(array, state),
fn write_json_object<W: fmt::Write, const PRETTY: bool>(
obj: &Object<'_>,
state.begin_object()?;
if !obj.0.is_empty() {
state.new_line()?;
for (index, entry) in obj.0.iter().enumerate() {
state.write_json(&entry.key)?;
state.write_object_key_end()?;
entry.value.write_json_value(state)?;
if index != obj.0.len() - 1 {
state.write(",")?;
state.end_object()
fn write_json_array<W: fmt::Write, const PRETTY: bool>(
array: &Vec<Self>,
state.begin_array()?;
if !array.is_empty() {
for (index, value) in array.iter().enumerate() {
value.write_json_value(state)?;
if index != array.len() - 1 {
state.end_array()
/// Converts this value to its JSON representation, with extra whitespace to
/// make it easier for a human to read.
/// This uses two spaces for indentation, and `\n` for end of lines. Use
/// [`to_json_pretty_custom()`](Self::to_json_pretty_custom) to customize
/// the formatting behavior.
/// # Panics
/// This function will panic if there is not enough memory to format the
pub fn to_json_pretty(&self) -> String {
let mut out = String::new();
self.pretty_write_json_to(&mut out).expect("out of memory");
out
pub fn to_json_pretty_custom(&self, indentation: &str, line_ending: &str) -> String {
self.pretty_write_json_to_custom(indentation, line_ending, &mut out)
.expect("out of memory");
/// Converts this value to its JSON representation, with no extraneous
/// whitespace.
pub fn to_json(&self) -> String {
self.write_json_to(&mut out).expect("out of memory");
/// Writes this value's JSON representation to `destination`, with no extraneous
pub fn write_json_to<W: fmt::Write>(&self, destination: W) -> fmt::Result {
self.write_json::<W, false>("", "", destination)
/// Writes this value's JSON representation to `destination`, with extra
/// whitespace to make it easier for a human to read.
pub fn pretty_write_json_to<W: fmt::Write>(&self, destination: W) -> fmt::Result {
self.pretty_write_json_to_custom(" ", "\n", destination)
pub fn pretty_write_json_to_custom<W: fmt::Write>(
self.write_json::<W, true>(indentation, line_ending, destination)
macro_rules! impl_as_number {
($name:ident, $type:ident) => {
impl Value<'_> {
/// Parses the contained value as an
#[doc = concat!("[`", stringify!($type), "`]")]
/// if possible.
/// If the source number is a floating point number or has a negative sign,
/// this will always return None.
pub fn $name(&self) -> Option<$type> {
self.as_number().and_then(JsonNumber::$name)
};
impl_as_number!(as_u8, u8);
impl_as_number!(as_u16, u16);
impl_as_number!(as_u32, u32);
impl_as_number!(as_u64, u64);
impl_as_number!(as_u128, u128);
impl_as_number!(as_usize, usize);
impl_as_number!(as_i8, i8);
impl_as_number!(as_i16, i16);
impl_as_number!(as_i32, i32);
impl_as_number!(as_i64, i64);
impl_as_number!(as_i128, i128);
impl_as_number!(as_isize, isize);
#[test]
fn value_ases() {
assert!(Value::from(true).as_bool().unwrap());
assert_eq!(
Value::String(JsonString::from_json("\"\"").unwrap())
.as_string()
.unwrap(),
""
);
.as_str()
Value::Number(JsonNumber::from_json("1").unwrap())
.as_number()
.unwrap()
.as_u64()
1
Value::Object(Object::new()).as_object().unwrap(),
&Object::new()
assert_eq!(Value::Array(Vec::new()).as_array().unwrap(), &[]);
assert!(Value::Null.is_null());
assert!(!Value::from(true).is_null());
assert_eq!(Value::Null.as_bool(), None);
assert_eq!(Value::Null.as_number(), None);
assert_eq!(Value::Null.as_string(), None);
assert_eq!(Value::Null.as_str(), None);
assert_eq!(Value::Null.as_object(), None);
assert_eq!(Value::Null.as_array(), None);
impl<'a> Display for Value<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
self.pretty_write_json_to(f)
self.write_json_to(f)
pub(crate) struct ValueParser;
impl<'a> ParseDelegate<'a> for ValueParser {
type Array = Vec<Value<'a>>;
type Error = Infallible;
type Key = JsonString<'a>;
type Object = Object<'a>;
type Value = Value<'a>;
fn null(&mut self) -> Result<Self::Value, Self::Error> {
Ok(Value::Null)
fn boolean(&mut self, value: bool) -> Result<Self::Value, Self::Error> {
Ok(Value::Boolean(value))
fn number(&mut self, value: JsonNumber<'a>) -> Result<Self::Value, Self::Error> {
Ok(Value::Number(value))
fn string(&mut self, value: JsonString<'a>) -> Result<Self::Value, Self::Error> {
Ok(Value::String(value))
fn begin_object(&mut self) -> Result<Self::Object, Self::Error> {
Ok(Object::default())
fn object_key(
&mut self,
_object: &mut Self::Object,
key: JsonString<'a>,
) -> Result<Self::Key, Self::Error> {
Ok(key)
fn object_value(
object: &mut Self::Object,
key: Self::Key,
value: Self::Value,
) -> Result<(), Self::Error> {
object.push(Entry { key, value });
Ok(())
fn object_is_empty(&self, object: &Self::Object) -> bool {
object.is_empty()
fn end_object(&mut self, object: Self::Object) -> Result<Self::Value, Self::Error> {
Ok(Value::Object(object))
fn begin_array(&mut self) -> Result<Self::Array, Self::Error> {
Ok(Vec::new())
fn array_value(
array: &mut Self::Array,
array.push(value);
fn array_is_empty(&self, array: &Self::Array) -> bool {
array.is_empty()
fn end_array(&mut self, array: Self::Array) -> Result<Self::Value, Self::Error> {
Ok(Value::Array(array))
fn kind_of(&self, value: &Self::Value) -> JsonKind {
match value {
Value::Number(_) => JsonKind::Number,
Value::String(_) => JsonKind::String,
Value::Boolean(_) => JsonKind::Boolean,
Value::Object(_) => JsonKind::Object,
Value::Array(_) => JsonKind::Array,
Value::Null => JsonKind::Null,
struct WriteState<'a, W, const PRETTY: bool> {
writer: W,
level: usize,
indent_per_level: &'a str,
line_ending: &'a str,
is_at_line_start: bool,
impl<'a, W, const PRETTY: bool> WriteState<'a, W, PRETTY>
where
W: fmt::Write,
{
fn new(writer: W, indentation: &'a str, line_ending: &'a str) -> Self {
Self {
writer,
level: 0,
is_at_line_start: true,
indent_per_level: indentation,
line_ending,
fn write(&mut self, str: &str) -> fmt::Result {
if PRETTY && self.is_at_line_start {
self.is_at_line_start = false;
for _ in 0..self.level {
self.writer.write_str(self.indent_per_level)?;
self.writer.write_str(str)?;
fn write_json(&mut self, str: &JsonString<'_>) -> fmt::Result {
write!(self.writer, "\"{}\"", str.as_json())
fn new_line(&mut self) -> fmt::Result {
if PRETTY {
self.write(self.line_ending)?;
self.is_at_line_start = true;
fn begin_object(&mut self) -> fmt::Result {
self.write("{")?;
self.level += 1;
fn write_object_key_end(&mut self) -> fmt::Result {
self.write(": ")?;
self.write(":")?;
fn end_object(&mut self) -> fmt::Result {
self.level -= 1;
self.write("}")?;
fn begin_array(&mut self) -> fmt::Result {
self.write("[")?;
fn end_array(&mut self) -> fmt::Result {
self.write("]")?;
impl<'a> Index<usize> for Value<'a> {
type Output = Value<'a>;
/// This function panics if the value is not a [`Value::Array`] or if the
/// index is out of bounds of the array.
fn index(&self, index: usize) -> &Self::Output {
self.get_index(index).expect("index not found")
impl<'a> IndexMut<usize> for Value<'a> {
/// is a [`Value::Array`].
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.get_index_mut(index).expect("index not found")
impl<'b, 'a> Index<&'b str> for Value<'a> {
/// This function panics if this value is not a [`Value::Object`] or if the
/// `key` is not found.
fn index(&self, index: &'b str) -> &Self::Output {
self.get(index).expect("key not found")
impl<'b, 'a> IndexMut<&'b str> for Value<'a> {
/// `key`, if this is a [`Value::Object`].
fn index_mut(&mut self, index: &'b str) -> &mut Self::Output {
self.get_mut(index).expect("key not found")
impl<'a> From<bool> for Value<'a> {
fn from(value: bool) -> Self {
Self::Boolean(value)
impl<'a> From<Object<'a>> for Value<'a> {
fn from(value: Object<'a>) -> Self {
Self::Object(value)
impl<'a> From<Vec<Value<'a>>> for Value<'a> {
fn from(value: Vec<Value<'a>>) -> Self {
Self::Array(value)
impl<'a> From<&'a str> for Value<'a> {
fn from(value: &'a str) -> Self {
Self::from(JsonString::from(value))
impl<'a> From<String> for Value<'a> {
fn from(value: String) -> Self {
impl<'a> From<JsonString<'a>> for Value<'a> {
fn from(value: JsonString<'a>) -> Self {
Self::String(value)
/// A JSON Object (list of key-value pairs).
/// value's contents instead of multiple random accesses, the iteration should
/// be preferred. Additional options to make random access faster in
/// environments that can support it [are being considered][issue] for future
/// releases.
/// # Modifying an Object
/// Because [`Object`] internally is just a `Vec<Entry>`, it implements
/// `Deref`/`DerefMut` to its internal storage. This means that all of `Vec`'s
/// methods are available to change the contents of an object.
/// ```rust
/// use justjson::{Entry, Object, Value};
/// let mut obj = Object::new();
/// obj.push(Entry::new("hello", "world"));
/// assert_eq!(Value::Object(obj).to_json(), r#"{"hello":"world"}"#)
/// ```
pub struct Object<'a>(Vec<Entry<'a>>);
impl<'a> Default for Object<'a> {
fn default() -> Self {
Self::new()
impl<'a> Object<'a> {
/// Returns an empty object.
pub const fn new() -> Self {
Self(Vec::new())
/// Returns an empty object that can store up to `capacity` elements without
/// reallocating.
pub fn with_capacity(capacity: usize) -> Self {
Self(Vec::with_capacity(capacity))
/// Returns the value associated with `key`, if found.
self.iter()
.find_map(|entry| (entry.key == key).then_some(&entry.value))
/// Returns a mutable reference to the value associated with `key`, if
/// found.
self.get_entry_mut(key).map(|entry| &mut entry.value)
/// Returns a mutable reference to the entry associated with `key`, if
pub fn get_entry_mut(&mut self, key: &str) -> Option<&mut Entry<'a>> {
self.iter_mut().find(|entry| entry.key == key)
impl<'a> Deref for Object<'a> {
type Target = Vec<Entry<'a>>;
fn deref(&self) -> &Self::Target {
&self.0
impl<'a> DerefMut for Object<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
impl<'a> FromIterator<(JsonString<'a>, Value<'a>)> for Object<'a> {
fn from_iter<T: IntoIterator<Item = (JsonString<'a>, Value<'a>)>>(iter: T) -> Self {
iter.into_iter()
.map(|(key, value)| Entry { key, value })
.collect()
impl<'a> FromIterator<Entry<'a>> for Object<'a> {
fn from_iter<T: IntoIterator<Item = Entry<'a>>>(iter: T) -> Self {
Self(iter.into_iter().collect())
/// An key-value pair in an [`Object`].
pub struct Entry<'a> {
/// The key of this entry.
pub key: JsonString<'a>,
/// The value associated with the key.
pub value: Value<'a>,
impl<'a> Entry<'a> {
/// Returns a new entry for the given key and value.
pub fn new(key: impl Into<JsonString<'a>>, value: impl Into<Value<'a>>) -> Self {
key: key.into(),
value: value.into(),
fn primitive_values() {
assert_eq!(Value::from_json("true").unwrap(), Value::from(true));
assert_eq!(Value::from_json("false").unwrap(), Value::from(false));
assert_eq!(Value::from_json("null").unwrap(), Value::Null);
fn objects() {
Value::from_json("{}").unwrap(),
Value::Object(Object::default())
Value::from_json(r#"{"hello":"world"}"#).unwrap(),
Value::Object(Object::from_iter([(
JsonString::from_json(r#""hello""#).unwrap(),
Value::String(JsonString::from_json(r#""world""#).unwrap())
)]))
Value::from_json(r#" { "hello" : "world" , "another" : "value" }"#).unwrap(),
Value::Object(Object::from_iter([
Entry::new(
),
JsonString::from_json(r#""another""#).unwrap(),
Value::String(JsonString::from_json(r#""value""#).unwrap())
)
]))
fn cow() {
let mut value =
Value::from_json_bytes(br#"{"a":1,"b":true,"c":"hello","d":[],"e":{}}"#).unwrap();
value["b"] = Value::from(false);
let root = value.as_object_mut().unwrap();
root[0].key = JsonString::from("newa");
root[0].value = JsonString::from("a").into();
let Value::Array(d_array) = &mut root[3].value else {
unreachable!()
d_array.push(Value::Null);
// Replace the newly inserted null (uses IndexMut on the array).
value["d"][0] = Value::from(false);
let generated = value.to_json();
generated,
r#"{"newa":"a","b":false,"c":"hello","d":[false],"e":{}}"#
fn index() {
let mut value = Value::from_json_bytes(br#"{"b":true,"a":[false]}"#).unwrap();
assert_eq!(value["b"], Value::from(true));
assert_eq!(value.get_index_mut(0), None);
assert_eq!(value["a"][0], Value::from(false));
assert_eq!(value["a"].get_mut("a"), None);
fn froms() {
assert_eq!(Value::from(true), Value::Boolean(true));
assert_eq!(Value::from(Object::new()), Value::Object(Object::new()));
assert_eq!(Value::from(Vec::new()), Value::Array(Vec::new()));
Value::from(String::from("a")),
Value::String(JsonString::from("a"))
assert_eq!(Value::from("a"), Value::String(JsonString::from("a")));
Value::from(JsonString::from("a")),
fn as_es() {
macro_rules! test_as {
($as:ident) => {
Value::Number(JsonNumber::from_json("1").unwrap()).$as(),
Some(1)
test_as!(as_i8);
test_as!(as_i16);
test_as!(as_i32);
test_as!(as_i64);
test_as!(as_i128);
test_as!(as_isize);
test_as!(as_u8);
test_as!(as_u16);
test_as!(as_u32);
test_as!(as_u64);
test_as!(as_u128);
test_as!(as_usize);
assert!(
Value::Number(JsonNumber::from_json("0").unwrap())
.as_f32()
.abs()
< f32::EPSILON
.as_f64()
< f64::EPSILON