serde_json_merge 0.0.7

Merge, index, iterate, and sort a serde_json::Value (recursively)
//! Recursive and non-recursive merge operations for JSON values.

pub mod union;

use crate::index::{Index, Path as IndexPath};
use crate::iter::Traverser;
use serde_json::Value;
pub use union::Union;

/// Merges one JSON value into another.
pub trait Merge: Sized {
    /// Merges immediate children from `other` into this value.
    fn merge<T>(&mut self, other: &Self)
    where
        T: Traverser;

    /// Merges all descendants from `other` into this value.
    fn merge_recursive<T>(&mut self, other: &Self)
    where
        T: Traverser;

    /// Merges immediate children using a caller-provided policy.
    fn merge_by<T, F>(&mut self, other: &Self, merge: &mut F)
    where
        T: Traverser,
        F: FnMut(&IndexPath, &mut Value, Option<&Value>) -> bool;

    /// Merges all descendants using a caller-provided policy.
    fn merge_by_recursive<T, F>(&mut self, other: &Self, merge: &mut F)
    where
        T: Traverser,
        F: FnMut(&IndexPath, &mut Value, Option<&Value>) -> bool;

    /// Returns this value after merging immediate children from `other`.
    #[inline]
    #[must_use]
    fn merged<T>(mut self, other: &Self) -> Self
    where
        T: Traverser,
    {
        self.merge::<T>(other);
        self
    }

    /// Returns this value after recursively merging `other`.
    #[inline]
    #[must_use]
    fn merged_recursive<T>(mut self, other: &Self) -> Self
    where
        T: Traverser,
    {
        self.merge_recursive::<T>(other);
        self
    }

    /// Returns this value after applying a custom merge policy to immediate children.
    #[inline]
    #[must_use]
    fn merged_by<T, F>(mut self, other: &Self, merge: &mut F) -> Self
    where
        T: Traverser,
        F: FnMut(&IndexPath, &mut Value, Option<&Value>) -> bool,
    {
        self.merge_by::<T, F>(other, merge);
        self
    }

    /// Returns this value after applying a custom policy recursively.
    #[inline]
    #[must_use]
    fn merged_by_recursive<T, F>(mut self, other: &Self, merge: &mut F) -> Self
    where
        T: Traverser,
        F: FnMut(&IndexPath, &mut Value, Option<&Value>) -> bool,
    {
        self.merge_by_recursive::<T, F>(other, merge);
        self
    }
}

impl Merge for Value {
    #[inline]
    fn merge<T>(&mut self, other: &Self)
    where
        T: Traverser,
    {
        self.merge_by::<T, _>(other, &mut merge_func);
    }

    #[inline]
    fn merge_recursive<T>(&mut self, other: &Self)
    where
        T: Traverser,
    {
        self.merge_by_recursive::<T, _>(other, &mut merge_func);
    }

    #[inline]
    fn merge_by<T, F>(&mut self, other: &Self, merge: &mut F)
    where
        T: Traverser,
        F: FnMut(&IndexPath, &mut Value, Option<&Value>) -> bool,
    {
        let mut traverser = T::new();
        traverser.set_limit(None);
        traverser.set_depth(1);
        while traverser
            .process_next(other, |idx, new_value| {
                if let Some(value) = self.get_index_mut(idx) {
                    merge(idx, value, new_value)
                } else {
                    true
                }
            })
            .is_some()
        {}
    }

    #[inline]
    fn merge_by_recursive<T, F>(&mut self, other: &Self, merge: &mut F)
    where
        T: Traverser,
        F: FnMut(&IndexPath, &mut Value, Option<&Value>) -> bool,
    {
        let mut traverser = T::new();
        traverser.set_limit(None);
        traverser.set_depth(None);
        while traverser
            .process_next(other, |idx, new_value| {
                if let Some(value) = self.get_index_mut(idx) {
                    merge(idx, value, new_value)
                } else {
                    true
                }
            })
            .is_some()
        {}
    }
}

fn merge_func(_idx: &IndexPath, this: &mut Value, other: Option<&Value>) -> bool {
    match (this, other) {
        // add new fields when merging two objects
        (&mut Value::Object(ref mut this), Some(Value::Object(other))) => {
            for k in other.keys() {
                this.entry(k.clone()).or_insert(Value::Null);
            }
            true
        }
        // extend array with other array
        (&mut Value::Array(ref mut this), Some(Value::Array(other))) => {
            this.extend(other.clone());
            false
        }
        // extend array with other value
        (&mut Value::Array(ref mut this), Some(other)) => {
            this.extend([other.clone()]);
            false
        }
        // do not overwrite anything with null
        (_, Some(&Value::Null)) => false,
        // overwrite this with other
        (this, Some(other)) => {
            *this = other.clone();
            false
        }
        _ => false,
    }
}

#[cfg(test)]
mod test {
    use super::Merge;
    use crate::iter::dfs::Dfs;
    use pretty_assertions::assert_eq;
    use serde_json::json;

    #[test]
    fn merge_array_string() {
        let base = json!(["a", "b"]);
        let merge = json!(["b", "c"]);
        assert_eq!(
            &base.merged_recursive::<Dfs>(&merge),
            &json!(["a", "b", "b", "c"])
        );
    }

    #[test]
    fn merge_array_object() {
        let base = json!([{"value": "a"}, {"value": "b"}]);
        let merge = json!([{"value": "b"}, {"value": "c"}]);
        assert_eq!(
            &base.merged_recursive::<Dfs>(&merge),
            &json!([
                {"value": "a"},
                {"value": "b"},
                {"value": "b"},
                {"value": "c"}
            ])
        );
    }

    #[test]
    fn merge_object() {
        let base = json!({"value1": "a", "value2": "b"});
        let merge = json!({"value1": "a", "value2": "c", "value3": "d"});
        assert_eq!(
            &base.merged_recursive::<Dfs>(&merge),
            &json!({
                "value1": "a",
                "value2": "c",
                "value3": "d",
            })
        );
    }

    #[test]
    fn merge_string() {
        let base = json!("a");
        let merge = json!("b");
        assert_eq!(&base.merged_recursive::<Dfs>(&merge), &merge);
    }
}