rust 如何合并(包括嵌套数组值)两个serde_yaml::Value对象?

0qx6xfy6  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(133)

我在创建一个递归函数来解析两个serde_yaml::Value变量并将它们合并时遇到了麻烦。在一个基本级别的对象上将它们合并组合起来很容易,但是子级别的值只是组合值的值。
给出:

let original:serde_yaml::Value = serde_yaml::from_str(r#"
keyA:
  subKeyA:
    - A
    - B
    - C
keyB: "one"
keyC: "a"
"#
).unwrap();

let add_or_modify_these_values:serde_yaml::Value = serde_yaml::from_str(r#"
keyA:
  subKeyA:
    - D
  subKeyB:
    - BA
keyB: "two"
keyC:
  - A
  - B
"#
).unwrap();

字符串
如何将它们合并组合起来,以便考虑所有嵌套属性,例如:

keyA:
  subKeyA:
    - A
    - B
    - C
    - D
  subKeyB:
    - BA
keyB: "two"
keyC:
  - A
  - B


当有复杂的情况时(例如,不同的值类型,如keyC),我更喜欢用新的值类型替换原始的值类型。
编辑:我在这里也看了一个类似的json问题:How can I merge two JSON objects with Rust?,但是这个合并方法不会合并组合数组值,只会覆盖。

xv8emn3q

xv8emn3q1#

我已经弄清楚了下面的转换(可能需要一些清理):

fn merge_yaml(a: &mut serde_yaml::Value, b: serde_yaml::Value) {
    match (a, b) {
        (a @ &mut serde_yaml::Value::Mapping(_), serde_yaml::Value::Mapping(b)) => {
            let a = a.as_mapping_mut().unwrap();
            for (k, v) in b {
                if v.is_sequence() && a.contains_key(&k) && a[&k].is_sequence() { 
                    let mut _b = a.get(&k).unwrap().as_sequence().unwrap().to_owned();
                    _b.append(&mut v.as_sequence().unwrap().to_owned());
                    a[&k] = serde_yaml::Value::from(_b);
                    continue;
                }
                if !a.contains_key(&k) {a.insert(k.to_owned(), v.to_owned());}
                else { merge_yaml(&mut a[&k], v); }

            }
            
        }
        (a, b) => *a = b,
    }
}

字符串

23c0lvtd

23c0lvtd2#

编辑:正确的答案已标记。将把这个留给JSON版本。
JSON版本:

fn merge(a: &mut serde_json::Value, b: serde_json::Value) {
    match (a, b) {
        (a @ &mut serde_json::Value::Object(_), serde_json::Value::Object(b)) => {
            let a = a.as_object_mut().unwrap();
            for (k, v) in b {
                if v.is_array() && a.contains_key(&k) && a.get(&k).as_ref().unwrap().is_array() {
                           let mut _a = a.get(&k).unwrap().as_array().unwrap().to_owned();
                           _a.append(&mut v.as_array().unwrap().to_owned());
                           a[&k] = serde_json::Value::from(_a);
                }
                else{   
                    merge(a.entry(k).or_insert(serde_json::Value::Null), v);
                }
            }
        }
        (a, b) => *a = b,
    }
}

字符串

9vw9lbht

9vw9lbht3#

下面是一个清理后的版本(与#![feature(let_chains)]一起使用):

fn merge_yaml(a: &mut serde_yaml::Value, b: serde_yaml::Value) {
        match (a, b) {
            (serde_yaml::Value::Mapping(ref mut a), serde_yaml::Value::Mapping(b)) => {
                for (k, v) in b {
                    if let Some(b_seq) = v.as_sequence()
                        && let Some(a_val) = a.get(&k)
                        && let Some(a_seq) = a_val.as_sequence()
                    {
                        a[&k] = [a_seq.as_slice(), b_seq.as_slice()].concat().into();
                        continue;
                    }

                    if !a.contains_key(&k) {
                        a.insert(k, v);
                    }
                    else {
                        Self::merge_yaml(&mut a[&k], v);
                    }
                }

            }
            (a, b) => *a = b,
        }
    }

字符串

wydwbb8l

wydwbb8l4#

已经有一个清理过的版本,但是它使用了#![feature(let_chains)],这并不理想。我提出了我自己的清理过的版本,我相信它更具可读性,并且更多地利用了match语句。

// This is just an alias I did in my own code, feel free to remove it
use serde_yaml::Value as Yalue;

/// Merge the `src` yaml into the `dst` yaml, overwriting `dst`'s values when keys overlap.
fn merge_into(src: &Yalue, dst: &mut Yalue) {
    match (src, dst) {
        // if value is a mapping, add all keys of src into dst.
        (Yalue::Mapping(src), Yalue::Mapping(dst)) => {
            for (key, src_val) in src {
                match dst.get_mut(key) {
                    // if a key is present in both, we recursively merge the values.
                    Some(dst_val) => merge_into(src_val, dst_val),

                    // otherwise, we insert the src key into the dst.
                    None => _ = dst.insert(key.clone(), src_val.clone()),
                };
            }
        }

        // if value is a sequence, add all elements of src into dst.
        (Yalue::Sequence(src), Yalue::Sequence(dst)) => {
            dst.extend_from_slice(src);
        }

        // otherwise, the src value overwrites the dst value.
        (src, dst) => *dst = src.clone(),
    }
}

字符串

相关问题