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

如何在Rust中动态构建serde_json树并保留节点引用?

Rust中使用serde_json动态构建层级JSON结构的解决方案

问题背景

在Rust开发中,使用serde_json在循环里动态创建JSON结构:每次迭代默认生成一个Value::Object节点作为前一节点的同级,但有时需要将当前节点设为前一节点的子节点。尝试保留前一节点的可变引用时,会和JSON结构内的节点引用冲突;用Rc<RefCell>包装节点,只是把编译时问题转移到运行时,需要更合理的解决方式。

预期JSON输出

{
  "children": [
    {
      "name": "name1"
    },
    {
      "name": "name2",
      "children": [
        {
          "name": "name3"
        }
      ]
    },
    {
      "name": "name4"
    }
  ]
}

对应输入结构

- name1
- name2 
    - name3
- name4

简化示例代码

use serde_json::{Value, Map};
use std::borrow::Borrow;

fn node(name: &str) -> Map<String, Value> {
    let mut node: Map<String, Value> = Map::new();
    node.insert("name".to_string(), Value::String(name.to_string()));
    node
}

fn main() {
    let mut root: Map<String, Value> = Map::new();
    root.insert("children".to_string(), Value::Array(Vec::new()));

    let mut parent: &mut Vec<Value>;
    
    if let Some(Value::Array(array)) = root.get_mut("children") {
        parent = array;
    } else {
        std::process::exit(1);
    }
    
    // 第一次迭代
    {
        let node = node("1");
        parent.push(Value::Object(node));
    }
    
    // 第二次迭代
    {
        let node = node("2");
        let mut array = Vec::new();
        array.push(Value::Object(node));

        if let Some(Value::Object(obj)) = parent.last_mut() {
            obj.insert("children".to_string(), Value::Array(array));
            if let Some(Value::Array(array)) = obj.get_mut("children") {
                parent = array;
            }
        }
    }
    
    // 第三次迭代
    {
        let node = node("3");
        // parent.push(Value::Object(node));
    }
    
    let json = serde_json::to_string_pretty(&*root.borrow()).unwrap();
    println!("{}", json);
}

解决方案:用栈跟踪当前父节点数组

核心思路是用一个栈(Vec<&mut Vec<Value>>)记录当前所在层级的父节点数组,栈的结构天然匹配层级的进入与退出逻辑,完全符合Rust的借用规则,无需运行时检查。

修改后的实现代码

use serde_json::{Value, Map};

fn node(name: &str) -> Map<String, Value> {
    let mut node = Map::new();
    node.insert("name".to_string(), Value::String(name.to_string()));
    node
}

fn main() {
    let mut root = Map::new();
    root.insert("children".to_string(), Value::Array(Vec::new()));

    // 栈顶始终指向当前要添加节点的父数组
    let mut parent_stack: Vec<&mut Vec<Value>> = Vec::new();
    if let Some(Value::Array(array)) = root.get_mut("children") {
        parent_stack.push(array);
    } else {
        std::process::exit(1);
    }

    // 模拟输入结构的迭代流程
    // 1. 添加name1(同级)
    parent_stack.last_mut().unwrap().push(Value::Object(node("name1")));

    // 2. 添加name2,进入其子层级
    parent_stack.last_mut().unwrap().push(Value::Object(node("name2")));
    // 获取刚添加的name2节点,创建children数组并压入栈
    if let Some(Value::Object(obj)) = parent_stack.last().unwrap().last_mut() {
        obj.insert("children".to_string(), Value::Array(Vec::new()));
        if let Some(Value::Array(array)) = obj.get_mut("children") {
            parent_stack.push(array);
        }
    }

    // 3. 添加name3(子级)
    parent_stack.last_mut().unwrap().push(Value::Object(node("name3")));

    // 4. 回到父层级(弹出栈顶)
    parent_stack.pop();

    // 5. 添加name4(同级)
    parent_stack.last_mut().unwrap().push(Value::Object(node("name4")));

    // 输出最终JSON
    let json = serde_json::to_string_pretty(&root).unwrap();
    println!("{}", json);
}

方案优势

  • 编译时安全:所有引用操作都符合Rust的借用规则,无需Rc<RefCell>带来的运行时开销与panic风险。
  • 逻辑清晰:栈的压入/弹出操作完美对应层级的进入/退出,和输入结构的缩进逻辑天然匹配。
  • 易于扩展:如果是处理逐行输入的场景,可根据每行缩进深度动态调整栈的状态:
    • 缩进加深:压入当前节点的children数组
    • 缩进不变:保持栈顶不变
    • 缩进变浅:弹出对应次数的栈顶元素

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 21:12:12