From 72eb1d01667bd1d349e0ea0dc693684db226424f Mon Sep 17 00:00:00 2001 From: erhodes Date: Tue, 19 Jul 2022 15:05:09 -0600 Subject: [PATCH 1/3] added remove_by_pointer method and associated doctest --- src/value/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/value/mod.rs b/src/value/mod.rs index 4793e93ec..ffa578722 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -828,6 +828,63 @@ impl Value { }) } + /// Removes a value by a JSON Pointer and returns that value. + /// + /// JSON Pointer defines a string syntax for identifying a specific value + /// within a JavaScript Object Notation (JSON) document. + /// + /// A Pointer is a Unicode string with the reference tokens separated by `/`. + /// Inside tokens `/` is replaced by `~1` and `~` is replaced by `~0`. The + /// addressed value is returned and if there is no such value `None` is + /// returned. + /// + /// For more information, read [RFC6901](https://tools.ietf.org/html/rfc6901). + /// + /// # Example of Use + /// + /// ``` + /// # use serde_json::{Value, json}; + /// # + /// let mut data = json!({ + /// "x": { + /// "y": ["z", "zz"] + /// } + /// }); + /// + /// let v = data.remove_by_pointer("/x/y/0"); + /// assert_eq!(v, Some(json!("z"))); + /// assert_eq!( + /// data, + /// json!({ + /// "x": { + /// "y": ["zz"] + /// } + /// }), + /// ); + /// + /// let v = data.remove_by_pointer("/x/y"); + /// assert_eq!(v, Some(json!(["zz"]))); + /// assert_eq!(data, json!({ "x": {} })); + /// + /// let v = data.remove_by_pointer(""); + /// assert_eq!(v, Some(json!({ "x": {} }))); + /// assert_eq!(data, Value::Null); + /// ``` + pub fn remove_by_pointer(&mut self, pointer: &str) -> Option { + if pointer.is_empty() { + return Some(self.take()); + } + pointer.rsplit_once('/').and_then(|(pointer, key)| { + self.pointer_mut(pointer).and_then(|value| match value { + Value::Object(map) => map.remove(key), + Value::Array(list) => { + parse_index(key).and_then(move |x| (x < list.len()).then(|| list.remove(x))) + } + _ => None, + }) + }) + } + /// Takes the value out of the `Value`, leaving a `Null` in its place. /// /// ``` From 24db0d5399635cbc342099336be842eb07aad388 Mon Sep 17 00:00:00 2001 From: erhodes Date: Tue, 19 Jul 2022 15:28:25 -0600 Subject: [PATCH 2/3] manual impl of rsplit_once for older compiler compatibility --- src/value/mod.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index ffa578722..a6baf2dc5 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -874,14 +874,16 @@ impl Value { if pointer.is_empty() { return Some(self.take()); } - pointer.rsplit_once('/').and_then(|(pointer, key)| { - self.pointer_mut(pointer).and_then(|value| match value { - Value::Object(map) => map.remove(key), - Value::Array(list) => { - parse_index(key).and_then(move |x| (x < list.len()).then(|| list.remove(x))) - } - _ => None, - }) + #[allow(clippy::manual_split_once)] + let mut pointer_split = pointer.rsplitn(2, '/'); + let key = pointer_split.next()?; + let pointer = pointer_split.next()?; + self.pointer_mut(pointer).and_then(|value| match value { + Value::Object(map) => map.remove(key), + Value::Array(list) => { + parse_index(key).and_then(move |x| (x < list.len()).then(|| list.remove(x))) + } + _ => None, }) } From 634b62f6a30a89af6ee5f67ed0f0a1829f59ece9 Mon Sep 17 00:00:00 2001 From: erhodes Date: Tue, 19 Jul 2022 15:36:43 -0600 Subject: [PATCH 3/3] manual impl of bool::then for older compiler --- src/value/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index a6baf2dc5..3d33b13e9 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -880,9 +880,13 @@ impl Value { let pointer = pointer_split.next()?; self.pointer_mut(pointer).and_then(|value| match value { Value::Object(map) => map.remove(key), - Value::Array(list) => { - parse_index(key).and_then(move |x| (x < list.len()).then(|| list.remove(x))) - } + Value::Array(list) => parse_index(key).and_then(move |x| { + if x < list.len() { + Some(list.remove(x)) + } else { + None + } + }), _ => None, }) }