You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何连接Rc<RefCell<T>>树节点并动态构建serde_json::Value

问题分析

你遇到的核心矛盾是:serde_json::Value是值语义类型——当你把一个Value转移或克隆到另一个Value中时,两者会变成独立的对象,后续修改原对象不会同步到目标对象。而你需要的是引用语义:让data.sub和sub指向同一个内存对象,修改任意一方都会同步到另一方。

当前代码中使用sub.take()会直接转移sub内部Value的所有权,导致sub被重置为空,后续对sub的修改完全和data无关,这就是为什么最终输出只有a字段的原因。

解决方案:自定义共享JSON节点类型

我们可以通过Rc<RefCell<T>>实现所有权共享,同时自定义一个支持嵌套、动态修改的JSON节点类型,最终可以转换为标准的serde_json::Value输出。

步骤1:定义共享节点类型

这个类型支持对象、数组、原始值三种节点,所有可修改的节点都用Rc<RefCell>包裹,实现引用语义:

use serde::{Serialize, Serializer};
use serde_json::{Value, json, Map};
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt;

#[derive(Clone)]
enum SharedJson {
    Object(Rc<RefCell<Map<String, SharedJson>>>),
    Array(Rc<RefCell<Vec<SharedJson>>>),
    Primitive(Rc<RefCell<Value>>),
}

impl SharedJson {
    // 创建空对象节点
    fn new_object() -> Self {
        SharedJson::Object(Rc::new(RefCell::new(Map::new())))
    }

    // 创建原始值节点(如布尔、数字、字符串)
    fn new_primitive(val: Value) -> Self {
        SharedJson::Primitive(Rc::new(RefCell::new(val)))
    }

    // 向对象节点插入字段
    fn insert(&self, key: &str, value: SharedJson) -> Option<SharedJson> {
        match self {
            SharedJson::Object(map) => map.borrow_mut().insert(key.to_string(), value),
            _ => panic!("只能向对象节点插入字段"),
        }
    }

    // 修改原始值节点的内容
    fn mutate_primitive<F: FnOnce(&mut Value)>(&self, f: F) {
        match self {
            SharedJson::Primitive(val) => f(&mut val.borrow_mut()),
            _ => panic!("只能修改原始值节点"),
        }
    }

    // 转换为标准serde_json::Value
    fn to_value(&self) -> Value {
        match self {
            SharedJson::Object(map) => {
                let mut obj = Map::new();
                for (key, val) in map.borrow().iter() {
                    obj.insert(key.clone(), val.to_value());
                }
                Value::Object(obj)
            }
            SharedJson::Array(arr) => {
                let mut vec = Vec::new();
                for val in arr.borrow().iter() {
                    vec.push(val.to_value());
                }
                Value::Array(vec)
            }
            SharedJson::Primitive(val) => val.borrow().clone(),
        }
    }
}

// 支持直接序列化
impl Serialize for SharedJson {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.to_value().serialize(serializer)
    }
}

// 支持Debug打印
impl fmt::Debug for SharedJson {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.to_value().fmt(f)
    }
}

步骤2:构建并修改动态结构

现在可以用SharedJson实现你的需求,后续修改子节点会自动同步到父节点:

fn main() {
    // 创建根对象节点
    let data = SharedJson::new_object();
    // 创建子对象节点
    let sub = SharedJson::new_object();

    // 先给sub添加a字段
    sub.insert("a", SharedJson::new_primitive(json!(true)));

    // 将sub关联到data中(克隆Rc,共享同一个内存对象)
    data.insert("sub", sub.clone());

    // 后续给sub添加b字段,修改会同步到data.sub
    sub.insert("b", SharedJson::new_primitive(json!(true)));

    // 输出完整结构
    println!("{:#?}", data.to_value());
}

输出结果

Object {
    "sub": Object {
        "a": Bool(true),
        "b": Bool(true),
    },
}
关键原理
  • Rc<RefCell<T>>实现了所有权共享和内部可变性,让多个变量可以指向同一个可修改的对象
  • 自定义SharedJson类型封装了共享逻辑,同时提供了转换为标准Value的方法,满足最终序列化需求
  • 插入子节点时克隆Rc,而不是转移所有权,保证父节点和子节点始终指向同一个内存对象

内容的提问来源于stack exchange,提问作者Raywell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 16:55:27