【问题标题】:How can I merge two JSON objects with Rust?如何将两个 JSON 对象与 Rust 合并?
【发布时间】:2017-11-02 08:40:11
【问题描述】:

我有两个 JSON 文件:

JSON 1

{
  "title": "This is a title",
  "person" : {
    "firstName" : "John",
    "lastName" : "Doe"
  },
  "cities":[ "london", "paris" ]
}

JSON 2

{
  "title": "This is another title",
  "person" : {
    "firstName" : "Jane"
  },
  "cities":[ "colombo" ]
}

我想将 #2 合并到 #1 中,其中 #2 覆盖 #1,产生以下输出:

{
  "title": "This is another title",
  "person" : {
    "firstName" : "Jane",
    "lastName" : "Doe"
  },
  "cities":[ "colombo" ]
}

我检查了板条箱json-patch,它可以做到这一点,但它不能针对稳定的 Rust 进行编译。是否可以使用 serde_json 和稳定的 Rust 做类似的事情?

【问题讨论】:

    标签: json rust serde json-patch serde-json


    【解决方案1】:

    由于您想使用json-patch,我假设您专门寻找JSON Merge Patch (RFC 7396) 实现,因为那是该板条箱实现的。在这种情况下,合并一个对象应该取消设置补丁中对应值为null的那些键,其他答案中的代码示例没有实现。

    解释该问题的代码如下。我修改了补丁以删除person.lastName 键,将其设置为null 作为演示。与其他答案之一不同,它也不需要unwrap() as_object_mut() 返回的Option。

    use serde_json::{json, Value};
    
    fn merge(a: &mut Value, b: Value) {
        if let Value::Object(a) = a {
            if let Value::Object(b) = b {
                for (k, v) in b {
                    if v.is_null() {
                        a.remove(&k);
                    }
                    else {
                        merge(a.entry(k).or_insert(Value::Null), v);
                    }
                } 
    
                return;
            }
        }
    
        *a = b;
    }
    
    fn main() {
        let mut a = json!({
            "title": "This is a title",
            "person" : {
                "firstName" : "John",
                "lastName" : "Doe"
            },
            "cities":[ "london", "paris" ]
        });
    
        let b = json!({
            "title": "This is another title",
            "person" : {
                "firstName" : "Jane",
                "lastName": null
            },
            "cities":[ "colombo" ]
        });
    
        merge(&mut a, b);
        println!("{:#}", a);
    }
    

    预期的输出是

    {
      "cities": [
        "colombo"
      ],
      "person": {
        "firstName": "Jane"
      },
      "title": "This is another title"
    }
    

    通知person.lastName已取消设置。

    【讨论】:

      【解决方案2】:

      将Shepmaster建议的答案放在下面

      #[macro_use]
      extern crate serde_json;
      
      use serde_json::Value;
      
      fn merge(a: &mut Value, b: Value) {
          match (a, b) {
              (a @ &mut Value::Object(_), Value::Object(b)) => {
                  let a = a.as_object_mut().unwrap();
                  for (k, v) in b {
                      merge(a.entry(k).or_insert(Value::Null), v);
                  }
              }
              (a, b) => *a = b,
          }
      }
      
      fn main() {
          let mut a = json!({
              "title": "This is a title",
              "person" : {
                  "firstName" : "John",
                  "lastName" : "Doe"
              },
              "cities":[ "london", "paris" ]
          });
      
          let b = json!({
              "title": "This is another title",
              "person" : {
                  "firstName" : "Jane"
              },
              "cities":[ "colombo" ]
          });
      
          merge(&mut a, b);
          println!("{:#}", a);
      }
      

      【讨论】:

      • 该算法不正确,因为它不会删除补丁中值为空的键。在递归调用merge之前需要检查v.is_null()
      【解决方案3】:

      这对我有用

      #[macro_use]
      extern crate serde_json;
      
      use serde_json::Value;
      
      fn merge(a: &mut Value, b: &Value) {
          match (a, b) {
              (&mut Value::Object(ref mut a), &Value::Object(ref b)) => {
                  for (k, v) in b {
                      merge(a.entry(k.clone()).or_insert(Value::Null), v);
                  }
              }
              (a, b) => {
                  *a = b.clone();
              }
          }
      }
      
      fn main() {
          let mut a = json!({
              "title": "This is a title",
              "person" : {
                  "firstName" : "John",
                  "lastName" : "Doe"
              },
              "cities":[ "london", "paris" ]
          });
      
          let b = json!({
              "title": "This is another title",
              "person" : {
                  "firstName" : "Jane"
              },
              "cities":[ "colombo" ]
          });
      
          merge(&mut a, &b);
          println!("{:#}", a);
      }
      

      【讨论】:

      • 好像如果你改成fn merge(a: Value, b: Value) -> Value你可以避免克隆。
      • @Shepmaster 您能否在此处添加一个包含您上述建议的答案?我是 Rust 新手,还不太明白你在说什么。如果你愿意,我会接受你的回答。
      • Something like this,虽然我有点难过需要unwrap。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-14
      • 1970-01-01
      • 2016-03-16
      • 2019-08-31
      • 2021-01-11
      相关资源
      最近更新 更多