新闻中心

C++如何实现一个简单的JSON解析器_从零开始编写C++递归下降JSON解析器

2025-11-30
浏览次数:
返回列表
答案:本文介绍如何用C++从零实现一个简单的JSON解析器,核心是定义支持null、boolean、number、string、array和object的JsonValue类型,使用std::variant存储不同类型,并通过递归下降解析法为每种类型编写解析函数,最终组合成完整解析器。代码包含数据结构定义、解析逻辑和测试示例,适合理解JSON结构与编译原理基础。

c++如何实现一个简单的json解析器_从零开始编写c++递归下降json解析器

想用C++从零实现一个简单的JSON解析器?关键在于理解JSON的结构和递归下降的基本思路。JSON支持几种基本类型:null、boolean、number、string、array 和 object,我们可以为每种类型写一个解析函数,通过递归组合起来。整个过程不需要复杂的工具,纯手工编写即可。

1. 定义JSON数据结构

首先,我们需要一个能表示任意JSON值的C++类型。使用 std::variant 是个好选择,它能安全地存储不同类型的数据。

#include <string>
#include <vector>
#include <map>
#include <variant>
#include <memory>
<p>// 前向声明解析器类
struct JsonValue;</p><p>using JsonObject = std::map<std::string, JsonValue>;
using JsonArray = std::vector<JsonValue>;
using JsonNumber = double;
using JsonString = std::string;</p><p>// 核心类型:一个JSON值可以是 null, bool, number, string, array, object
struct JsonValue {
std::variant<std::nullptr_t, bool, JsonNumber, JsonString, JsonArray, JsonObject> value;</p><pre class="brush:php;toolbar:false;">// 构造函数
JsonValue() : value(nullptr) {}
JsonValue(std::nullptr_t) : value(nullptr) {}
JsonValue(bool b) : value(b) {}
JsonValue(double d) : value(d) {}
JsonValue(const std::string& s) : value(s) {}
JsonValue(JsonArray a) : value(std::move(a)) {}
JsonValue(JsonObject o) : value(std::move(o)) {}

// 类型判断辅助函数
bool is_null() const { return std::holds_alternative<std::nullptr_t>(value); }
bool is_bool() const { return std::holds_alternative<bool>(value); }
bool is_number() const { return std::holds_alternative<JsonNumber>(value); }
bool is_string() const { return std::holds_alternative<JsonString>(value); }
bool is_array() const { return std::holds_alternative<JsonArray>(value); }
bool is_object() const { return std::holds_alternative<JsonObject>(value); }

// 获取值(需确保类型正确)
bool as_bool() const { return std::get<bool>(value); }
double as_number() const { return std::get<double>(value); }
const std::string& as_string() const { return std::get<std::string>(value); }
const JsonArray& as_array() const { return std::get<JsonArray>(value); }
const JsonObject& as_object() const { return std::get<JsonObject>(value); }

};

2. 实现递归下降解析器

我们定义一个解析器类,维护当前解析位置和输入字符串。每个JSON类型对应一个解析函数。

class JsonParser {
private:
    const std::string& input;
    size_t pos;
<pre class="brush:php;toolbar:false;">// 跳过空白字符
void skip_whitespace() {
    while (pos < input.size() && (input[pos] == ' ' || input[pos] == '	' || input[pos] == '
' || input[pos] == ''))
        ++pos;
}

// 匹配并消耗指定字符
bool match(char c) {
    skip_whitespace();
    if (pos < input.size() && input[pos] == c) {
        ++pos;
        return true;
    }
    return false;
}

// 解析 "null"
JsonValue parse_null() {
    if (input.substr(pos, 4) == "null") {
        pos += 4;
        return JsonValue(nullptr);
    }
    throw std::runtime_error("Expected 'null'");
}

// 解析 "true" / "false"
JsonValue parse_boolean() {
    if (input.substr(pos, 4) == "true") {
        pos += 4;
        return JsonValue(true);
    }
    if (input.substr(pos, 5) == "false") {
        pos += 5;
        return JsonValue(false);
    }
    throw std::runtime_error("Expected 'true' or 'false'");
}

// 解析数字(简单版本,支持整数和小数)
JsonValue parse_number() {
    size_t start = pos;
    if (pos < input.size() && input[pos] == '-') ++pos;

    if (pos == input.size() || !isdigit(input[pos]))
        throw std::runtime_error("Invalid number");

    while (pos < input.size() && isdigit(input[pos])) ++pos;

    if (pos < input.size() && input[pos] == '.') {
        ++pos;
        if (pos == input.size() || !isdigit(input[pos]))
            throw std::runtime_error("Invalid number");
        while (pos < input.size() && isdigit(input[pos])) ++pos;
    }

    std::string numStr = input.substr(start, pos - start);
    try {
        return JsonValue(std::stod(numStr));
    } catch (...) {
        throw std::runtime_error("Invalid number format");
    }
}

// 解析带引号的字符串(未处理转义字符)
JsonValue parse_string() {
    if (!match('"')) throw std::runtime_error("Expected '"'");

    size_t start = pos;
    while (pos < input.size() && input[pos] != '"') {
        ++pos;
    }

    if (pos == input.size()) throw std::runtime_error("Unterminated string");

    std::string str = input.substr(start, pos - start);
    ++pos; // 跳过结尾的 "
    return JsonValue(str);
}

// 解析数组
JsonValue parse_array() {
    if (!match('[')) throw std::runtime_error("Expected '['");

    JsonArray arr;
    skip_whitespace();

    if (match(']')) return JsonValue(arr); // 空数组

    while (true) {
        arr.push_back(parse_value());
        skip_whitespace();

        if (match(']')) break;
        if (!match(',')) throw std::runtime_error("Expected ',' or ']'");
    }

    return JsonValue(arr);
}

// 解析对象
JsonValue parse_object() {
    if (!match('{')) throw std::runtime_error("Expected '{'");

    JsonObject obj;
    skip_whitespace();

    if (match('}')) return JsonValue(obj); // 空对象

    while (true) {
        JsonValue keyVal = parse_string();
        std::string key = keyVal.as_string();

        skip_whitespace();
        if (!match(':')) throw std::runtime_error("Expected ':'");

        JsonValue value = parse_value();
        obj[key] = value;

        skip_whitespace();
        if (match('}')) break;
        if (!match(',')) throw std::runtime_error("Expected ',' or '}'");
    }

    return JsonValue(obj);
}

// 主解析入口
JsonValue parse_value() {
    skip_whitespace();
    if (pos >= input.size()) throw std::runtime_error("Unexpected end of input");

    char c = input[pos];
    if (c == 'n') return parse_null();
    if (c == 't' || c == 'f') return parse_boolean();
    if (c == '-' || isdigit(c)) return parse_number();
    if (c == '"') return parse_string();
    if (c == '[') return parse_array();
    if (c == '{') return parse_object();

    throw std::runtime_error("Unexpected character: " + std::string(1, c));
}

public: explicit JsonParser(const std::string& str) : input(str), pos(0) {}

JsonValue parse() {
    JsonValue result = parse_value();
    skip_whitespace();
    if (pos != input.size())
        throw std::runtime_error("Extra characters after JSON");
    return result;
}

};

3. 使用示例

写个简单的main函数测试一下:

GoEnhance GoEnhance

全能AI视频制作平台:通过GoEnhance AI让视频创作变得比以往任何时候都更简单。

GoEnhance 347 查看详情 GoEnhance

#include <iostream>
#include <stdexcept>
<p>void print_json(const JsonValue& val, int indent = 0) {
std::string space(indent, ' ');
if (val.is_null()) {
std::cout << "null";
} else if (val.is_bool()) {
std::cout << (val.as_bool() ? "true" : "false");
} else if (val.is_number()) {
std::cout << val.as_number();
} else if (val.is_string()) {
std::cout << """ << val.as_string() << """;
} else if (val.is_array()) {
std::cout << "[
";
const auto& arr = val.as_array();
for (size_t i = 0; i < arr.size(); ++i) {
std::cout << std::string(indent + 2, ' ');
print_json(arr[i], indent + 2);
if (i != arr.size() - 1) std::cout << ",";
std::cout << "
";
}
std::cout << std::string(indent, ' ') << "]";
} else if (val.is_object()) {
std::cout << "{
";
const auto& obj = val.as_object();
auto it = obj.begin();
while (it != obj.end()) {
std::cout << std::string(indent + 2, ' ') << """ << it->first << "": ";
print_json(it->second, indent + 2);
++it;
if (it != obj.end()) std::cout << ",";
std::cout << "
";
}
std::cout << std::string(indent, ' ') << "}";
}
}</p><p>int main() {
std::string json_str = R"({
"name": "Alice",
"age": 30,
"is_student": false,
"grades": [85.5, 92.0, 78.5],
"address": {
"city": "Beijing",
"zipcode": "100000"
},
"spouse": null
})";</p><pre class="brush:php;toolbar:false;">try {
    JsonParser parser(json_str);
    JsonValue root = parser.parse();
    print_json(root);
    std::cout << std::endl;
} catch (const std::exception& e) {
    std::cerr << "Parse error: " << e.what() << std::endl;
    return 1;
}

return 0;

}

4. 注意事项与改进方向

这个解析器是教学级的,但已具备核心功能。你可以在此基础上扩展:

  • 支持字符串中的转义字符(如 , ", \)
  • 支持科学计数法(如 1e5)
  • 添加位置信息便于报错定位
  • 实现序列化(将 JsonValue 转回字符串)
  • 优化性能,避免 substr 频繁拷贝

基本上就这些。递归下降的关键是“一个函数管一种语法结构”,逻辑清晰,容易调试。自己写一遍,对理解JSON和编译原理都很有帮助。

以上就是C++如何实现一个简单的JSON解析器_从零开始编写C++递归下降JSON解析器的详细内容,更多请关注其它相关文章!


# 尼克  # 新北区网站建设价格  # 一个企业seo网站的优化流  # 琼海商城网站优化  # 官方网站建设公司外包  # 公司网站推广员  # 武清酒店网站建设  # 承德一站式网站推广电话  # 网站建设入门吉他app  # 网络营销推广计划模板  # 揭阳营销网络推广招聘  # 如何将  # 不同类型  # 跳过  # js  # 并在  # 从零开始  # 如何实现  # 器中  # 数据结构  # 递归  # stream  # ios  # c++  # ai  # 工具  # json  # git 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 4399体育竞技小游戏_4399小游戏赛事入口  迅雷下载到U盘速度很慢怎么办_迅雷U盘下载慢优化方法  NRF24L01数据传输深度解析:解决大载荷接收异常与分包策略  在Go Martini框架中高效服务动态生成图像的实践指南  我的世界官方游戏入口 我的世界官网平台直达链接  飞书妙记怎样用语音转文字速记_飞书妙记用语音转文字速记【速记方法】  CSS子选择器:如何区分并样式化嵌套列表的子层级  126邮箱网页版官方入口 126邮箱账号在线登录平台  Fabric模组开发:自定义物品与物品组的现代管理方法  顺丰快递查询系统 官方正版查询入口  “音游” × “怪文书” 题材的节奏冒险游戏 《晕晕电波症候群》确定于2026年4月发售!  邮编格式怎么匹配地址_根据邮编格式快速匹配详细地址的技巧  Django AJAX 文件上传教程:解决图片无法保存到模型的常见问题  俄罗斯方块最新版入口 俄罗斯方块在线玩官网入口  百度网盘网页版入口 百度网盘网页版官方登录网址  拼多多视频播放卡顿如何处理 拼多多视频播放优化技巧  Pygame教程:解决用户输入与游戏状态更新不同步问题  C++指针和引用有什么区别_C++内存管理核心概念深度解析  Golang如何安装Swagger工具_GoSwagger文档生成环境  Win11输入法不见了怎么办_Windows11恢复语言栏显示方法  PHP表单数据传递:如何通过隐藏输入字段获取动态ID  Win10桌面图标出现小盾牌怎么办 Win10去除UAC图标教程【解决】  fishbowl官网免费版 fishbowl养鱼网站入口  Node.js CSV 数据处理:基于字段值条件过滤整条记录的策略  小猿搜题在线学习页面在哪_小猿搜题在线学习中心入口  c++ 获取系统当前时间 c++时间戳获取方法  CSS实现侧边栏导航项全宽圆角悬停背景效果  c++如何实现单例设计模式_c++线程安全的单例模式写法  三星GalaxyZFold5怎样在相册制作折叠屏分镜_iPhone三星GalaxyZFold5相册制作折叠屏分镜【创意编辑】  在J*aScript中复现SciPy的B样条拟合与求值:关键考量  如何修改开机登录密码_Windows账户安全设置超详细教程【必学】  J*aScript中向JSON对象添加新属性的正确姿势  在VS Code中配置和运行Dart程序的完整步骤  1688商家版怎样分析买家画像精准供货_1688商家版分析买家画像精准供货【供货策略】  Python getattr() 异常处理深度解析:避免程序意外退出  在Socket.IO连接中实现Access Token自动更新与动态重连  提升屏幕阅读器对“m”时间单位的播报准确性:HTML与CSS组合解决方案  如何在 Excel Online 和 Google 表格中更改日期格式  PHP中获取MongoDB服务器运行时间(Uptime)的专业指南  苹果手机如何防止被恶意App追踪  c++中的std::basic_string的SSO优化_c++短字符串优化深度解析  css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染  必由学网页版入口 必由学官方平台直接访问  CSS Flexbox与媒体查询:实现响应式布局中元素的并排与堆叠  如何使用纯J*aScript判断Input元素是否在特定类容器内  Go语言中Map存储的结构体如何调用指针方法:深入解析与实践  淘宝网网页版登录入口 淘宝官方网页版快捷登录  Golang如何优化内存分配与垃圾回收_Golang内存管理与GC优化实践  12306选座怎么选到商务座_12306商务座选择与配置说明  俄罗斯浏览器官网直达链接 俄罗斯浏览器最新在线入口导航 

搜索