You need to enable JavaScript to run this app. 如何使用PHP递归处理富文本JSON语法树完成API侧校验与内容加工?
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PHP递归处理富文本JSON语法树完成API侧校验与内容加工?

结构化语法树处理实现方案

结构规则对齐

从给出的语法树示例可以归纳出三类节点:

  • 纯文本字符串:无结构化标记的普通文字内容
  • 结构化标记对象:带有type/value字段的自定义节点,包含样式类(加粗、斜体)、自定义元素类(图片、@提及)两类
  • 嵌套数组容器:分层对应整个文档、段落区块、单行内容、节点值集合

核心处理逻辑

因为整个语法树是完全递归嵌套的结构,不需要写复杂的状态机,也不需要分步骤多次遍历,用统一递归遍历函数就能覆盖所有需求,单次遍历就能同时完成内容过滤、合法性校验、纯文本提取、通知信息收集,哪怕是几万字的长文档也不会有性能压力:

  1. 递归分支规则
    • 遍历到数组类型的内容:逐个处理数组内的子元素,把每个子元素的有效处理结果收集起来,空值直接过滤;如果数组处理完没有有效内容,就整体丢弃,不保留空结构
    • 遍历到纯字符串类型:直接把内容拼接到纯文本结果里,按照规则如果纯文本不需要存储进结构化语法树,就返回空值让它被过滤
    • 遍历到带type字段的结构化对象:按节点类型分支处理:
      • 加粗、斜体这类纯样式节点:直接递归处理它的value字段集合,把处理后的有效子节点透传回原结构,只要子节点不全为空就保留这个样式节点
      • 图片类自定义元素:先校验地址合规性(比如协议是否为HTTPS、域名是否在白名单内),校验通过就透传保留,纯文本结果里对应位置插入[图片]标记
      • @提及类自定义元素:校验用户名格式合法性,把合规的用户名单独存入通知列表用于后续触发提醒,纯文本结果里对应位置插入@用户名标记,处理合规的节点透传保留
  2. 结果收集机制
    整个递归函数在运行时,在函数外层维护两个临时收集器,不需要遍历两次语法树:
  • 纯文本收集器:遍历过程中把每个节点对应的文本内容逐次拼接,行与行、段落与段落之间自动加对应分隔符,最终生成和渲染效果一致的纯文本内容
  • 加工后语法树收集器:收集每个节点经过校验、过滤后的有效结构,自动剔除所有不合规、空值的节点,最终生成可以直接存储、回传给前端的新版语法树

边界注意事项

  • 递归深度控制:因为样式节点支持无限嵌套,需要设置递归深度阈值,避免遇到恶意构造的过深嵌套结构导致栈溢出
  • 空结构自动清理:如果一个父节点处理完后,所有子元素都被过滤成空,那这个父节点本身也要被丢弃,不要在最终存储的结构里留无意义的空节点
  • 未知节点拦截:不在白名单内的结构化节点直接丢弃,从入口阻断非法注入的内容
  • 纯文本拼接的格式对齐:处理每一行内容的时候,要把换行符自然拼接进去,不同section之间保留段落分隔,最终生成的纯文本读起来和实际渲染的效果一致,不要把所有文字都拼成一长串

核心参考代码(Node.js 侧)

// 外层临时收集器
let plainTextResult = [];
let mentionNotifyList = [];

function processTreeNodes(node, depth = 0) {
  // 设置最大递归深度,防止栈溢出
  if (depth > 100) return null;

  // 数组类型:逐个子元素处理
  if (Array.isArray(node)) {
    const validChildren = [];
    for (const child of node) {
      const processedChild = processTreeNodes(child, depth + 1);
      if (processedChild !== null) validChildren.push(processedChild);
    }
    // 无有效内容的空数组直接丢弃
    return validChildren.length ? validChildren : null;
  }

  // 纯字符串类型
  if (typeof node === 'string') {
    plainTextResult.push(node);
    // 纯文本节点不存入结构化语法树
    return null;
  }

  // 结构化对象类型
  if (typeof node === 'object' && node.type) {
    // 样式类节点透传
    if (['bold', 'italic'].includes(node.type)) {
      const processedValue = processTreeNodes(node.value, depth + 1);
      return processedValue ? { type: node.type, value: processedValue } : null;
    }

    // 图片节点校验
    if (node.type === 'image') {
      const isUrlValid = /^https:\/\/允许的图片域名/.test(node.value);
      if (!isUrlValid) return null;
      plainTextResult.push('[图片]');
      return { type: 'image', value: node.value };
    }

    // @提及节点处理
    if (node.type === 'mention') {
      const username = node.value;
      const isUsernameValid = /^[a-z0-9_]{3,20}$/.test(username);
      if (!isUsernameValid) return null;
      mentionNotifyList.push(username);
      plainTextResult.push(`@${username}`);
      return { type: 'mention', value: username };
    }
  }

  // 未知类型节点直接拦截
  return null;
}

// 入口调用
const finalStoredTree = processTreeNodes(originalInputTree);
const finalPlainText = plainTextResult.join('').replace(/(\s*\n){3,}/g, '\n\n');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 16:15:04