如何将两个JSON对象与Rust合并?

Har*_*aka 5 json rust json-patch serde serde-json

我有两个JSON文件:

JSON 1

{
  "title": "This is a title",
  "person" : {
    "firstName" : "John",
    "lastName" : "Doe"
  },
  "cities":[ "london", "paris" ]
}
Run Code Online (Sandbox Code Playgroud)

JSON 2

{
  "title": "This is another title",
  "person" : {
    "firstName" : "Jane"
  },
  "cities":[ "colombo" ]
}
Run Code Online (Sandbox Code Playgroud)

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

{
  "title": "This is another title",
  "person" : {
    "firstName" : "Jane",
    "lastName" : "Doe"
  },
  "cities":[ "colombo" ]
}
Run Code Online (Sandbox Code Playgroud)

我检查了crate json-patch,它做了这个,但它不能编译对稳定的Rust.有可能做类似serde_json和稳定的Rust 这样的东西吗?

Arn*_*ion 6

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

说明这一点的代码如下。我修改了补丁以person.lastName通过将其设置null为演示来删除密钥。与其他答案之一不同,它也不需要unwrap()Option返回as_object_mut()

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);
}
Run Code Online (Sandbox Code Playgroud)

预期的输出是

{
  "cities": [
    "colombo"
  ],
  "person": {
    "firstName": "Jane"
  },
  "title": "This is another title"
}
Run Code Online (Sandbox Code Playgroud)

通知person.lastName已取消。


Har*_*aka 5

在下面放置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);
}
Run Code Online (Sandbox Code Playgroud)