C++使用nlohmann/json库解析JSON数据应用示例

C++使用nlohmann/json库解析JSON数据应用示例

本章节介绍了如何在C++项目中使用nlohmann/json库来解析和处理JSON数据。我们将展示如何安装和配置该库,并通过封装的JsonHandler类来简化JSON数据的操作过程。

1. nlohmann/json库介绍

nlohmann/json 是一个用于现代C++的JSON库,由Niels Lohmann开发。它是一个单头文件库,使用C++11标准编写,具有以下特点:

  • 简洁的API:语法直观,像访问原生C++数据结构一样操作JSON
  • 类型安全:提供了类型检查和异常处理机制
  • 高性能:比许多其他JSON库更快
  • 零依赖:只需要包含一个头文件即可使用
  • 跨平台:支持Windows、Linux、macOS等操作系统

1.1 下载安装

nlohmann/json库可以通过多种方式获取和安装:

  1. 通过包管理器安装

    • vcpkg: vcpkg install nlohmann-json
    • Conan: conan install nlohmann_json/3.11.2
    • apt (Ubuntu/Debian): sudo apt install nlohmann-json3-dev
  2. 手动下载

1.2 配置方法

在项目中使用nlohmann库非常简单:

  1. 包含头文件

    cpp 复制代码
    #include <nlohmann/json.hpp>
    // 或者使用别名
    using json = nlohmann::json;
  2. CMake配置(如果通过包管理器安装):

    cmake 复制代码
    find_package(nlohmann_json CONFIG REQUIRED)
    target_link_libraries(main PRIVATE nlohmann_json::nlohmann_json)
  3. 手动配置

    • json.hpp文件放在项目目录中
    • 在代码中包含:#include "nlohmann/json.hpp"

在本项目中,我们直接将json.hpp文件包含在项目中,无需额外配置。

2. 应用场景

在本项目中,nlohmann库主要应用于以下场景:

2.1 解析HTTP响应中的JSON数据

当客户端向服务器发送GET或POST请求后,服务器返回的数据通常是JSON格式。我们需要使用nlohmann/json库来解析这些数据,提取有用信息。

2.2 格式化JSON数据用于展示

为了在WebView中更好地展示JSON数据,我们需要将其格式化为带有语法高亮的HTML格式。

2.3 构造JSON请求数据

在向服务器发送POST请求时,我们需要构造JSON格式的请求体数据。

2.4 处理文件列表等结构化数据

服务器返回的文件列表等信息通常以JSON数组形式提供,需要使用nlohmann库进行解析和处理。

3. 使用JsonHandler封装nlohmann/json库相关方法

为了更好地管理和重用JSON处理代码,我们创建了JsonHandler类来封装所有与nlohmann/json库相关的操作。

3.1 JsonHandler.h头文件

cpp 复制代码
#ifndef JSONHANDLER_H
#define JSONHANDLER_H

#include <wx/string.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;

class JsonHandler {
    public:
    JsonHandler();
    ~JsonHandler();

    // JSON处理相关方法
    void ParseJsonResponse(const std::string& response);
    wxString FormatJsonForHtml(const wxString& jsonStr, const bool& formatBit);
    wxString FormatJsonValue(const json& j);
    wxString FormatJsonValueFormat(const json& j, int indentLevel);
    wxString GenerateJsonHtml(const wxString& jsonResponse, const bool& formatBit);

    // 保存原始JSON数据
    void SetRawJsonData(const wxString& rawData) { m_rawJsonData = rawData; }
    wxString GetRawJsonData() const { return m_rawJsonData; }

    private:
    wxString m_rawJsonData;  // 保存原始JSON数据
    };

#endif // JSONHANDLER_H

3.2 JsonHandler.cpp实现文件

JsonHandler.cpp文件中实现了所有JSON处理方法:

  1. ParseJsonResponse:解析JSON响应数据
  2. FormatJsonForHtml:将JSON格式化为HTML显示格式
  3. FormatJsonValue:递归生成不带缩进的JSON HTML字符串
  4. FormatJsonValueFormat:递归生成带缩进的JSON HTML字符串
  5. GenerateJsonHtml:生成完整的JSON显示HTML页面

下面是这些方法的具体实现:

3.2.1 ParseJsonResponse方法实现
cpp 复制代码
// 解析 JSON 响应
void JsonHandler::ParseJsonResponse(const std::string& response) {
    // 尝试解析 JSON
    try {
        // 使用nlohmann库解析JSON字符串
        json j = json::parse(response);
        // 将 JSON 格式化为带缩进的字符串
        std::string json_str = j.dump(4);
        // 记录日志
        wxLogInfo(wxString::FromUTF8("JSON: %s"), wxString::FromUTF8(json_str.c_str()));
        // 检查是否存在 "message" 字段
        if (j.contains("message")) {
            // 获取message字段的值
            std::string message = j["message"];
            // 记录解析结果到日志
            wxLogInfo(wxString::FromUTF8("解析结果: %s"), wxString::FromUTF8(message.c_str()));
        } else {
            // 如果缺少message字段,记录错误日志
            wxLogInfo(wxString::FromUTF8("JSON格式错误: 缺少message字段"));
        }

        // 检查是否存在 "data" 数组
        if (j.contains("data") && j["data"].is_array()) {
            // 获取data数组
            json dataArray = j["data"];
            // 遍历数组中的每个元素
            for (const auto& item : dataArray) {
                // 检查元素是否包含name和age字段且类型正确
                if (item.contains("name") && item["name"].is_string() && item.contains("age") && item["age"].is_number()) {
                    // 提取name和age字段的值
                    std::string name = item["name"];
                    int age = item["age"];
                    // 在日志中显示name和age信息
                    wxLogMessage(wxString::FromUTF8("Name: %s, Age: %d"), wxString::FromUTF8(name.c_str()), age);
                }
            }
        } else {
            // 如果缺少data数组或格式不正确,记录错误日志
            wxLogInfo(wxString::FromUTF8("JSON格式错误: 缺少data数组或格式不正确"));
        }
    } catch (const json::parse_error& e) {
        // JSON 解析失败
        std::string parseErrorMsg = "JSON解析失败: " + std::string(e.what());
        wxLogError(wxString::FromUTF8(parseErrorMsg.c_str()));
    }
}
3.2.2 FormatJsonForHtml方法实现
cpp 复制代码
// 格式化JSON字符串用于HTML显示
wxString JsonHandler::FormatJsonForHtml(const wxString& jsonStr, const bool& formatBit) {
    try {
        // 确保使用UTF-8编码
        std::string utf8Json = std::string(jsonStr.ToUTF8());
        // 使用nlohmann库解析JSON字符串
        json j = json::parse(utf8Json);
        wxLogError("***************************************************");
        wxLogError(j.dump());
        wxLogError("***************************************************");

        // 递归生成带有HTML类名的JSON字符串
        wxString formattedJson;
        if (!formatBit) {
            // 使用不带缩进的方式格式化JSON
            formattedJson = FormatJsonValue(j);
        } else {
            // 使用带缩进的方式格式化JSON
            formattedJson = FormatJsonValueFormat(j, 0);
        }
        wxLogError(wxT("格式化JSON字符串成功!"));
        // 返回格式化后的JSON字符串
        return formattedJson;
    } catch (const json::parse_error& e) {
        // 记录JSON解析错误日志
        wxLogError("JSON Parse Err: %s", e.what());
        wxLogError(wxT("格式化JSON字符串失败!"));
        // 返回原始JSON字符串
        return jsonStr;
    }
}
3.2.3 FormatJsonValue方法实现
cpp 复制代码
// 递归生成带有HTML类名的JSON字符串(不带格式化)
wxString JsonHandler::FormatJsonValue(const json& j) {
    // 判断JSON值是否为对象类型
    if (j.is_object()) {
        // 如果是 JSON 对象
        wxString result = wxT("{");
        bool first = true;
        // 遍历对象中的每个键值对
        for (auto& [key, value] : j.items()) {
            if (!first) {
                // 如果不是第一个元素,添加逗号分隔符
                result += wxT(",");
            }
            first = false;

            // 添加键,使用特定CSS类名进行标记
            result += wxString::Format(wxT("<span class='json-key'>\"%s\"</span>: "), wxString::FromUTF8(key.c_str()));

            // 递归处理值
            result += FormatJsonValue(value);
        }
        result += wxT("}");
        wxLogError("----------------------------------------------------------------------");
        wxLogError(result);
        wxLogError("----------------------------------------------------------------------");
        // 返回格式化后的对象字符串
        return result;
    } else if (j.is_array()) {
        // 如果是 JSON 数组
        wxString result = wxT("[");
        bool first = true;
        // 遍历数组中的每个元素
        for (auto& item : j) {
            if (!first) {
                // 如果不是第一个元素,添加逗号分隔符
                result += wxT(",");
            }
            first = false;

            // 递归处理数组项
            result += FormatJsonValue(item);
        }
        result += wxT("]");
        // 返回格式化后的数组字符串
        return result;
    } else if (j.is_string()) {
        // 如果是 JSON 字符串
        std::string jsonString = j.get<std::string>();

        // 使用 wxString::FromUTF8 正确处理UTF-8编码的字符串
        wxString wxStringValue = wxString::FromUTF8(jsonString.c_str());
        wxLogMessage("字符串值: %s", wxStringValue);

        // 转义HTML特殊字符以防止在HTML中显示错误
        wxString escapedValue = wxStringValue;
        escapedValue.Replace("&", "&amp;");   // 转义&符号
        escapedValue.Replace("<", "&lt;");    // 转义<符号
        escapedValue.Replace(">", "&gt;");    // 转义>符号
        escapedValue.Replace("\"", "&quot;"); // 转义双引号
        escapedValue.Replace("'", "&#39;");   // 转义单引号

        // 使用特定CSS类名标记字符串值并返回
        return wxString::Format("<span class='json-string'>\"%s\"</span>", escapedValue);
    } else if (j.is_number_integer() || j.is_number_unsigned()) {
        // 如果是 JSON 数字(整数)
        // 使用特定CSS类名标记整数并返回
        return wxString::Format(wxT("<span class='json-number'>%lld</span>"), j.get<int64_t>());
    } else if (j.is_number_float()) {
        // 如果是 JSON 数字(浮点数)
        // 使用特定CSS类名标记浮点数并返回
        return wxString::Format(wxT("<span class='json-number'>%.6f</span>"), j.get<double>());
    } else if (j.is_boolean()) {
        // 如果是 JSON 布尔值
        // 根据布尔值返回true或false,并使用特定CSS类名标记
        return j.get<bool>() ? wxT("<span class='json-boolean'>true</span>") : wxT("<span class='json-boolean'>false</span>");
    } else if (j.is_null()) {
        // 如果是 JSON null
        // 使用特定CSS类名标记null值并返回
        return wxT("<span class='json-null'>null</span>");
    }
    // 对于其他类型,使用特定CSS类名标记并返回
    return wxString::Format("<span class='json-string'>%s</span>", j.dump().c_str());
}
3.2.4 FormatJsonValueFormat方法实现
cpp 复制代码
// 递归生成带有HTML类名的JSON字符串(带格式化和缩进)
wxString JsonHandler::FormatJsonValueFormat(const json& j, int indentLevel) {
    // 生成当前层级的缩进字符串(每级2个空格)
    wxString indent(indentLevel * 2, ' ');
    // 生成内层缩进字符串(比当前层级多一级)
    wxString indentInner((indentLevel + 1) * 2, ' ');

    // 判断JSON值是否为对象类型
    if (j.is_object()) {
        // 如果是 JSON 对象
        if (j.empty()) {
            // 如果对象为空,直接返回{}
            return wxT("{}");
        }

        wxString result = wxT("{\n");
        bool first = true;
        // 遍历对象中的每个键值对
        for (auto& [key, value] : j.items()) {
            if (!first) {
                // 如果不是第一个元素,添加逗号和换行符
                result += wxT(",\n");
            }
            first = false;

            // 添加键和值,带缩进
            result += indentInner + wxString::Format(
                wxT("<span class='json-key'>\"%s\"</span>: %s"),
                wxString::FromUTF8(key.c_str()),
                // 递归处理值,缩进层级加1
                FormatJsonValueFormat(value, indentLevel + 1)
            );
        }
        // 添加换行符、缩进和结束括号
        result += wxT("\n") + indent + wxT("}");
        // 返回格式化后的对象字符串
        return result;
    } else if (j.is_array()) {
        // 如果是 JSON 数组
        if (j.empty()) {
            // 如果数组为空,直接返回[]
            return wxT("[]");
        }

        wxString result = wxT("[\n");
        bool first = true;
        // 遍历数组中的每个元素
        for (auto& item : j) {
            if (!first) {
                // 如果不是第一个元素,添加逗号和换行符
                result += wxT(",\n");
            }
            first = false;

            // 添加数组项,带缩进
            result += indentInner + FormatJsonValueFormat(item, indentLevel + 1);
        }
        // 添加换行符、缩进和结束括号
        result += wxT("\n") + indent + wxT("]");
        // 返回格式化后的数组字符串
        return result;
    } else if (j.is_string()) {
        // 如果是 JSON 字符串
        std::string jsonString = j.get<std::string>();

        // 使用 wxString::FromUTF8 正确处理UTF-8编码的字符串
        wxString wxStringValue = wxString::FromUTF8(jsonString.c_str());
        wxLogMessage("字符串值: %s", wxStringValue);

        // 转义HTML特殊字符以防止在HTML中显示错误
        wxString escapedValue = wxStringValue;
        escapedValue.Replace("&", "&amp;");   // 转义&符号
        escapedValue.Replace("<", "&lt;");    // 转义<符号
        escapedValue.Replace(">", "&gt;");    // 转义>符号
        escapedValue.Replace("\"", "&quot;"); // 转义双引号
        escapedValue.Replace("'", "&#39;");   // 转义单引号

        // 使用特定CSS类名标记字符串值并返回
        return wxString::Format("<span class='json-string'>\"%s\"</span>", escapedValue);
    } else if (j.is_number_integer() || j.is_number_unsigned()) {
        // 如果是 JSON 数字(整数)
        // 使用特定CSS类名标记整数并返回
        return wxString::Format(wxT("<span class='json-number'>%lld</span>"), j.get<int64_t>());
    } else if (j.is_number_float()) {
        // 如果是 JSON 数字(浮点数)
        // 使用特定CSS类名标记浮点数并返回
        return wxString::Format(wxT("<span class='json-number'>%.6f</span>"), j.get<double>());
    } else if (j.is_boolean()) {
        // 如果是 JSON 布尔值
        // 根据布尔值返回true或false,并使用特定CSS类名标记
        return j.get<bool>() ? wxT("<span class='json-boolean'>true</span>") : wxT("<span class='json-boolean'>false</span>");
    } else if (j.is_null()) {
        // 如果是 JSON null
        // 使用特定CSS类名标记null值并返回
        return wxT("<span class='json-null'>null</span>");
    }

    // 对于其他类型,使用特定CSS类名标记并返回
    return wxString::Format("<span class='json-string'>%s</span>", j.dump().c_str());
}
3.2.5 GenerateJsonHtml方法实现
cpp 复制代码
// 生成 HTML 页面
wxString JsonHandler::GenerateJsonHtml(const wxString& jsonResponse, const bool& formatBit) {
    wxLogError("jsonResponse:---------------------------------------");
    wxLogError(jsonResponse);
    wxLogError("jsonResponse:---------------------------------------");
    // 格式化JSON数据
    wxString formattedJson = FormatJsonForHtml(jsonResponse, formatBit);

    // 构造完整的 HTML 字符串
    wxString html = wxString::Format(
        R"(<!DOCTYPE html>
<html>
<head>
    <meta charset='UTF-8'>
    <title>JSON响应</title>
    <style>
        body { 
            font-family: 'Segoe UI', Arial, sans-serif; 
            background-color: #f5f5f5; 
            margin: 0; 
            padding: 20px; 
        }
        .json-container { 
            background-color: white; 
            border-radius: 8px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.1); 
            padding: 20px; 
            overflow-x: auto; 
        }
        h1 { 
            color: #333; 
            border-bottom: 1px solid #eee; 
            padding-bottom: 10px; 
            margin-top: 0; 
        }
        pre { 
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 14px;
            line-height: 1.4;
            margin: 0;
            white-space: pre-wrap; /* 保留空格和换行 */
        }
        .json-key { color: #e74c3c; font-weight: bold; }
        .json-string { color: #27ae60; }
        .json-number { color: #3498db; font-weight: bold; }
        .json-boolean { color: #9b59b6; font-weight: bold; }
        .json-null { color: #95a5a6; font-weight: bold; }
        .toggle { cursor: pointer; }
    </style>
    <script src="./static/highlight.min.js"></script>
    <link rel="stylesheet" href="./static/default.min.css">
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            hljs.highlightAll();
            const toggles = document.querySelectorAll('.toggle');
            toggles.forEach(toggle => {
                toggle.addEventListener('click', function() {
                    const content = this.nextElementSibling;
                    if (content.style.display === 'none') {
                        content.style.display = 'block';
                    } else {
                        content.style.display = 'none';
                    }
                });
            });
        });
    </script>
</head>
<body>
    <h1>服务器响应</h1>
    <div class='json-container'><pre>%s</pre></div>
</body>
</html>)",
        formattedJson
    );

    // 日志输出,用于调试
    wxLogInfo(wxString::FromUTF8("生成的完整HTML:\n%s"), html);

    // 返回生成的HTML页面
    return html;
}

4. MyFrame.cpp调用JsonHandler中封装的各个方法示例

在主框架类MyFrame中,我们通过jsonHandler实例调用JsonHandler类中封装的方法。

4.1 示例1:处理GET请求返回的JSON数据

cpp 复制代码
// 获取文件列表的事件处理函数
void MyFrame::OnGetFileLists(wxCommandEvent& event) {
    // 发送GET请求获取文件列表
    std::string url = "/files";
    std::string response = httpClient.HttpGet(url);
    
    // 检查响应是否为空
    if (response.empty()) {
        // 如果返回的响应为空,可能是请求失败
        wxLogError("GET request failed or returned empty response.");
        m_textCtrl->SetValue("GET Request Failed: Empty response.");
        return;
        }
        
    // 保存原始JSON数据供后续使用
    jsonHandler.SetRawJsonData(wxString::FromUTF8(response.c_str()));
    
    // 在浏览器控件中显示格式化的JSON数据
    wxString html = jsonHandler.GenerateJsonHtml(wxString::FromUTF8(response.c_str()), false);
    m_webView->SetPage(html, "UTF-8");
    
    // 解析JSON数据并填充下拉框
    try {
        // 使用nlohmann库解析JSON字符串
        json j = json::parse(response);
        
        // 清空下拉框中的现有内容
        m_comboBox->Clear();

        // 检查是否存在 "data" 对象以及 "data.filelist" 数组
        if (j.contains("data") && j["data"].is_object()) {
            // 获取data对象
            json dataObj = j["data"];

            // 检查是否存在 "filelist" 数组
            if (dataObj.contains("filelist") && dataObj["filelist"].is_array()) {
                // 获取文件列表数组
                json fileArray = dataObj["filelist"];
                // 遍历数组中的每个文件项
                for (const auto& item : fileArray) {
                    // 检查项是否包含name字段且为字符串类型
                    if (item.contains("name") && item["name"].is_string()) {
                        // 提取文件名
                        std::string name = item["name"];
                        // 将文件名添加到下拉框中
                        m_comboBox->Append(wxString::FromUTF8(name.c_str()));
                        }
                    }
                }
            }
        }
    // 捕获JSON解析错误
    catch (const json::parse_error& e) {
        // JSON 解析失败
        std::string parseErrorMsg = "JSON解析失败: " + std::string(e.what());
        wxLogError(wxString::FromUTF8(parseErrorMsg.c_str()));
        }
    }

4.2 示例2:格式化显示JSON数据

cpp 复制代码
// 格式化JSON显示的事件处理函数
void MyFrame::OnFormatJson(wxCommandEvent& event) {
    // 使用格式化方式生成JSON HTML页面
    wxString html = jsonHandler.GenerateJsonHtml(jsonHandler.GetRawJsonData(), true);
    
    // 清除WebView的历史记录
    m_webView->ClearHistory();
    // 重新加载页面
    m_webView->Reload();
    // 在WebView中显示格式化的JSON数据
    m_webView->SetPage(html, "UTF-8");
    }

5. 总结

通过使用nlohmann/json库和封装的JsonHandler类,我们能够:

  1. 简化JSON数据的解析和处理过程
  2. 提供格式化的JSON数据显示功能
  3. 实现JSON数据的保存和重用
  4. 保证代码的模块化和可维护性

nlohmann/json库的使用大大简化了C++中JSON数据的处理,其直观的API设计使得JSON操作就像访问原生数据结构一样简单。通过封装在JsonHandler类中,我们实现了代码的重用和更好的维护性。

相关推荐
Peace & Love4873 小时前
C++初阶 -- 模拟实现list
开发语言·c++·笔记
似水এ᭄往昔5 小时前
【C++】--list的使用和模拟实现
开发语言·c++
十五年专注C++开发5 小时前
qtmqtt: 一个开源且好用的mqtt开源客户端
c++·qt·mqtt·开源
小苏兮5 小时前
【数据结构】二叉搜索树
开发语言·数据结构·c++·学习·1024程序员节
腾昵猫5 小时前
程序员的自我修养(三)
c++
晨曦(zxr_0102)6 小时前
CSP-X 2024 复赛编程题全解(B4104+B4105+B4106+B4107)
数据结构·c++·算法
·白小白6 小时前
力扣(LeetCode) ——15.三数之和(C++)
c++·算法·leetcode
无限进步_6 小时前
深入理解C语言scanf函数:从基础到高级用法完全指南
c语言·开发语言·c++·后端·算法·visual studio
m0_748240256 小时前
C++仿Muduo库Server服务器模块实现 基于Reactor模式的高性
服务器·c++·php