| use super::Value; |
| use crate::lib::*; |
| use crate::map::Map; |
| |
| /// 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(ref vec) => vec.get(*self), |
| _ => None, |
| } |
| } |
| fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { |
| match *v { |
| Value::Array(ref mut vec) => vec.get_mut(*self), |
| _ => None, |
| } |
| } |
| fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { |
| match *v { |
| Value::Array(ref mut 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(ref map) => map.get(self), |
| _ => None, |
| } |
| } |
| fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { |
| match *v { |
| Value::Object(ref mut 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(ref mut 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<'a, T> Index for &'a 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 super::String {} |
| impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} |
| } |
| |
| /// Used in panic messages. |
| struct Type<'a>(&'a Value); |
| |
| impl<'a> fmt::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<I> ops::Index<I> 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<I> ops::IndexMut<I> 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) |
| } |
| } |