use super::Value; use crate::map::Map; use alloc::borrow::ToOwned; use alloc::string::String; use core::fmt::{self, Display}; use core::ops; /// A type that can be used to index into a `serde_json::Value`. /// /// The [`get`] and [`get_mut`] methods of `Value` accept any type that /// implements `Index`, as does the [square-bracket indexing operator]. This /// trait is implemented for strings which are used as the index into a JSON /// map, and for `usize` which is used as the index into a JSON array. /// /// [`get`]: ../enum.Value.html#method.get /// [`get_mut`]: ../enum.Value.html#method.get_mut /// [square-bracket indexing operator]: ../enum.Value.html#impl-Index%3CI%3E /// /// This trait is sealed and cannot be implemented for types outside of /// `serde_json`. /// /// # Examples /// /// ``` /// # use serde_json::json; /// # /// let data = json!({ "inner": [1, 2, 3] }); /// /// // Data is a JSON map so it can be indexed with a string. /// let inner = &data["inner"]; /// /// // Inner is a JSON array so it can be indexed with an integer. /// let first = &inner[0]; /// /// assert_eq!(first, 1); /// ``` pub trait Index: private::Sealed { /// Return None if the key is not already in the array or object. #[doc(hidden)] fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>; /// Return None if the key is not already in the array or object. #[doc(hidden)] fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>; /// Panic if array index out of bounds. If key is not already in the object, /// insert it with a value of null. Panic if Value is a type that cannot be /// indexed into, except if Value is null then it can be treated as an empty /// object. #[doc(hidden)] fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value; } impl Index for usize { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { match v { Value::Array(vec) => vec.get(*self), _ => None, } } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { match v { Value::Array(vec) => vec.get_mut(*self), _ => None, } } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { match v { Value::Array(vec) => { let len = vec.len(); vec.get_mut(*self).unwrap_or_else(|| { panic!( "cannot access index {} of JSON array of length {}", self, len ) }) } _ => panic!("cannot access index {} of JSON {}", self, Type(v)), } } } impl Index for str { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { match v { Value::Object(map) => map.get(self), _ => None, } } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { match v { Value::Object(map) => map.get_mut(self), _ => None, } } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { if let Value::Null = v { *v = Value::Object(Map::new()); } match v { Value::Object(map) => map.entry(self.to_owned()).or_insert(Value::Null), _ => panic!("cannot access key {:?} in JSON {}", self, Type(v)), } } } impl Index for String { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { self[..].index_into(v) } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { self[..].index_into_mut(v) } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { self[..].index_or_insert(v) } } impl Index for &T where T: ?Sized + Index, { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { (**self).index_into(v) } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { (**self).index_into_mut(v) } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { (**self).index_or_insert(v) } } // Prevent users from implementing the Index trait. mod private { pub trait Sealed {} impl Sealed for usize {} impl Sealed for str {} impl Sealed for alloc::string::String {} impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} } /// Used in panic messages. struct Type<'a>(&'a Value); impl<'a> Display for Type<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match *self.0 { Value::Null => formatter.write_str("null"), Value::Bool(_) => formatter.write_str("boolean"), Value::Number(_) => formatter.write_str("number"), Value::String(_) => formatter.write_str("string"), Value::Array(_) => formatter.write_str("array"), Value::Object(_) => formatter.write_str("object"), } } } // The usual semantics of Index is to panic on invalid indexing. // // That said, the usual semantics are for things like Vec and BTreeMap which // have different use cases than Value. If you are working with a Vec, you know // that you are working with a Vec and you can get the len of the Vec and make // sure your indices are within bounds. The Value use cases are more // loosey-goosey. You got some JSON from an endpoint and you want to pull values // out of it. Outside of this Index impl, you already have the option of using // value.as_array() and working with the Vec directly, or matching on // Value::Array and getting the Vec directly. The Index impl means you can skip // that and index directly into the thing using a concise syntax. You don't have // to check the type, you don't have to check the len, it is all about what you // expect the Value to look like. // // Basically the use cases that would be well served by panicking here are // better served by using one of the other approaches: get and get_mut, // as_array, or match. The value of this impl is that it adds a way of working // with Value that is not well served by the existing approaches: concise and // careless and sometimes that is exactly what you want. impl ops::Index for Value where I: Index, { type Output = Value; /// Index into a `serde_json::Value` using the syntax `value[0]` or /// `value["k"]`. /// /// Returns `Value::Null` if the type of `self` does not match the type of /// the index, for example if the index is a string and `self` is an array /// or a number. Also returns `Value::Null` if the given key does not exist /// in the map or the given index is not within the bounds of the array. /// /// For retrieving deeply nested values, you should have a look at the /// `Value::pointer` method. /// /// # Examples /// /// ``` /// # use serde_json::json; /// # /// let data = json!({ /// "x": { /// "y": ["z", "zz"] /// } /// }); /// /// assert_eq!(data["x"]["y"], json!(["z", "zz"])); /// assert_eq!(data["x"]["y"][0], json!("z")); /// /// assert_eq!(data["a"], json!(null)); // returns null for undefined values /// assert_eq!(data["a"]["b"], json!(null)); // does not panic /// ``` fn index(&self, index: I) -> &Value { static NULL: Value = Value::Null; index.index_into(self).unwrap_or(&NULL) } } impl ops::IndexMut for Value where I: Index, { /// Write into a `serde_json::Value` using the syntax `value[0] = ...` or /// `value["k"] = ...`. /// /// If the index is a number, the value must be an array of length bigger /// than the index. Indexing into a value that is not an array or an array /// that is too small will panic. /// /// If the index is a string, the value must be an object or null which is /// treated like an empty object. If the key is not already present in the /// object, it will be inserted with a value of null. Indexing into a value /// that is neither an object nor null will panic. /// /// # Examples /// /// ``` /// # use serde_json::json; /// # /// let mut data = json!({ "x": 0 }); /// /// // replace an existing key /// data["x"] = json!(1); /// /// // insert a new key /// data["y"] = json!([false, false, false]); /// /// // replace an array value /// data["y"][0] = json!(true); /// /// // inserted a deeply nested key /// data["a"]["b"]["c"]["d"] = json!(true); /// /// println!("{}", data); /// ``` fn index_mut(&mut self, index: I) -> &mut Value { index.index_or_insert(self) } }