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

如何在C++串口数据流的任意字符串中提取JSON对象?

正确处理串口流式JSON数据的方案

绝对不要用正则表达式处理JSON——JSON的嵌套结构(对象、数组嵌套)属于上下文无关语法,正则表达式只能处理正则语言,无法正确跟踪嵌套层级,必然会在复杂场景下失效(比如你提到的含嵌入式JSON的情况)。

最优实现思路:流式缓冲区+层级计数+JSON库验证

针对串口无帧头、数据分批到达的特点,正确的做法是通过缓冲区累积数据,结合括号/引号的层级跟踪识别潜在的完整JSON对象,再用成熟的JSON库验证解析,具体步骤如下:

1. 核心逻辑

  • 维护一个持久化的输入缓冲区,串口收到的所有字节都追加到缓冲区,不丢弃任何数据。
  • 遍历缓冲区时,跟踪三个状态:
    • braces_depth:大括号{}的嵌套层级(遇到{加1,}减1)
    • brackets_depth:方括号[]的嵌套层级(遇到[加1,]减1)
    • in_string:标记当前是否处于JSON字符串中(遇到未转义的"时切换状态,字符串内的括号不计数)
  • 当遍历到某个位置时,若braces_depth == 0、brackets_depth == 0且in_string == false,说明此处可能是一个完整JSON对象的结尾,提取该段子串用JSON库验证。
  • 验证成功则处理该JSON对象,并从缓冲区移除已处理的部分;验证失败则继续遍历后续数据。

2. C++ 实现示例(基于nlohmann/json库)

nlohmann/json是轻量的单头文件JSON库,无需编译,直接包含即可使用:

#include <iostream>
#include <string>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

// 串口数据缓冲区(可作为类成员或全局变量)
std::string serial_buffer;

void process_serial_input(const std::string& new_bytes) {
    serial_buffer += new_bytes;

    size_t pos = 0;
    int braces_depth = 0;
    int brackets_depth = 0;
    bool in_string = false;
    bool escaped = false;

    while (pos < serial_buffer.size()) {
        char c = serial_buffer[pos];

        // 处理字符串内的字符(转义引号不触发状态切换)
        if (in_string) {
            if (escaped) {
                escaped = false;
            } else if (c == '\\') {
                escaped = true;
            } else if (c == '"') {
                in_string = false;
            }
            pos++;
            continue;
        }

        // 处理非字符串内的结构字符
        switch (c) {
            case '{': braces_depth++; break;
            case '}':
                braces_depth--;
                // 检查是否回到根层级,尝试提取并验证JSON
                if (braces_depth == 0 && brackets_depth == 0) {
                    std::string candidate = serial_buffer.substr(0, pos + 1);
                    try {
                        json parsed_json = json::parse(candidate);
                        // 解析成功,处理JSON对象
                        std::cout << "解析到完整JSON:\n" << parsed_json.dump(4) << "\n" << std::endl;
                        // 移除已处理的部分,重置状态重新遍历
                        serial_buffer = serial_buffer.substr(pos + 1);
                        pos = 0;
                        braces_depth = 0;
                        brackets_depth = 0;
                        in_string = false;
                        escaped = false;
                        continue;
                    } catch (const json::parse_error&) {
                        // 解析失败,说明不是完整对象,继续向后扫描
                    }
                }
                break;
            case '[': brackets_depth++; break;
            case ']': brackets_depth--; break;
            case '"': in_string = true; break;
            // 忽略空白字符,不影响JSON结构
            case ' ': case '\t': case '\n': case '\r': break;
            default: break;
        }
        pos++;
    }
}

// 模拟串口分批发送数据
int main() {
    // 分批发送嵌套JSON的片段
    process_serial_input("{\"a\": \"test\", \"b\": [1, ");
    process_serial_input("2, 3], \"c\": {\"d\": 4}}");
    // 模拟连续发送多个JSON对象
    process_serial_input("{\"x\": 5}{\"y\": 6");
    process_serial_input("}");

    return 0;
}

3. 关键优势

  • 适配串口流式数据:不管数据分多少次到达,只要缓冲区累积到完整JSON对象,就能正确识别。
  • 支持嵌套JSON:通过层级计数完美处理对象、数组的嵌套场景。
  • 容错性强:用JSON库验证确保不会误判不完整或语法错误的片段。
  • 性能高效:仅遍历缓冲区一次,验证成功后移除已处理数据,无需重复扫描。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 07:34:57