首页 前端知识 nlohmann::json从入门到精通

nlohmann::json从入门到精通

2024-05-05 12:05:00 前端知识 前端哥 461 800 我要收藏

nlohmann简介    

        nlohmann的起源与发展    

        与其他c++ json库的比较    

        nlohmann的架构与设计理 

nlohmann在C++中的应用    

        nlohmann的安装与引用    

        基本语法    

        高级语法

一、nlohmann简介 

1、nlohmann的起源与发展

nlohmann/json.hpp 的起源可以追溯到其创建者 Niels Lohmann 的个人需求。 Niels Lohmann 在使用其他 C++ JSON 库时,发现这些库要么过于复杂, 要么性能不佳,要么使用起来不够直观。因此,他决定自己开发一个满足 这些需求的 JSON 库。经过一段时间的开发和优化,nlohmann/json.hpp  逐渐成为了一个功能全面、性能卓越的 JSON 库。它支持 C++11 及更高版本 的标准,充分利用了现代 C++ 的特性,如范围 for循环、结构化绑定等, 使得操作 JSON 数据变得更加简单和直观。随着其在 GitHub 上的开源 和发布,nlohmann/json.hpp 迅速获得了广泛的关注和认可。大量的用户 和贡献者加入了这个项目,为其提供了丰富的功能和稳定的性能。如今, nlohmann/json.hpp 已经成为 C++ 社区中非常受欢迎的 JSON 库之一。

2、与其他c++ json库的比较

与其他 C++ JSON 库相比,nlohmann/json.hpp 有以下显著优势: 易用性:nlohmann/json.hpp 提供了直观且易于使用的 API,使得开发者 能够快速地理解和使用它。与其他库相比,它减少了学习成本,提高了 开发效率。 高性能:在解析和生成 JSON 数据方面,nlohmann/json.hpp 表现出色。 它的性能优于许多其他 C++ JSON 库,使得在处理大量 JSON 数据时 更加高效。 单文件库:nlohmann/json.hpp 是一个单文件库,无需额外的链接或构建 步骤。这使得它非常容易集成到任何 C++ 项目中,无论项目大小如何。

3、nlohmann的架构与设计理 念

nlohmann/json.hpp 的架构和设计理念主要体现在以下几个方面: 简洁性:库的设计力求简洁明了,避免不必要的复杂性和冗余。这使得代码易于阅读和维护,同时也降低了出错的可能性。 面向对象:库采用了面向对象的设计思想,将 JSON 数据表示为对象或数组。这使得开发者能够以更直观的方式操作 JSON 数据,提高了代码的可读性和可维护性。 灵活性:库提供了多种解析和生成 JSON 数据的方式,包括基于 SAX 和 DOM 的解析模式。这使得开发者能够根据自己的需求选择最合适的解析方式。 安全性:库在设计时充分考虑了安全性因素,避免了潜在的安全漏洞和错误。它提供了严格的类型检查和错误处理机制,确保了在处理 JSON 数据时的安全性和可靠性。

SAX是一种基于事件的解析方式。解析器在读取JSON/XML文档时,每当遇到特定的元素(如开始标签、结束标签、字符数据等),就会触发相应的事件。开发者需要编写事件处理函数来响应这些事件。SAX解析器不需要将整个文档加载到内存中,因此它非常适合处理大型JSON/XML文档。  DOM是一种基于树的解析方式。解析器将整个JSON/XML文档加载到内存中,并构建出一个与文档结构对应的树形结构(DOM树)。开发者可以通过遍历DOM树来访问和操作JSON/XML数据。由于DOM需要将整个文档加载到内存中,因此它更适合处理较小的JSON/XML文档

//SAX解析
struct MySax {
    bool null() {
        // 处理 null 值
        return true;
    }

    bool boolean(bool val) {
        // 处理 boolean 值
        return true;
    }

    bool number_integer(json::number_integer_t val) {
        // 处理整数
        return true;
    }

    bool number_unsigned(json::number_unsigned_t val) {
        // 处理无符号整数
        return true;
    }

    bool number_float(double val, const std::string& /*unused*/) {
        // 处理浮点数
        return true;
    }

    bool string(const std::string& val) {
        // 处理字符串
        std::cout << current_key << ": " << val << std::endl;
        return true;
    }

    bool start_object(size_t len) {
        // 处理对象开始
        return true;
    }

    bool key(const std::string& val) {
        // 处理对象的键
        current_key = val;
        return true;
    }

    bool end_object() {
        // 处理对象结束
        return true;
    }

    bool start_array(size_t len) {
        // 处理数组开始
        std::cout<<"array start"<<std::endl;
        return true;
    }

    bool end_array() {
        // 处理数组结束
        std::cout<<"array end"<<std::endl;
        return true;
    }

    bool parse_error(size_t len, std::string str, exception e)
    {
        return true;
    }

    bool binary(const std::vector<std::uint8_t>& data) {
        // 处理二进制数据
        // 例如,可以将数据转换为字符串或进行其他操作
        return true;
    }

private:
    std::string current_key; // 存储当前处理的键
    std::list<std::string> m_array;
};

#include "nlohmann/json.hpp"
#include <iostream>
#include <fstream>
#include <sstream>

void main()
{
    std::string jsonStr = R"(
           {
               "name": "John",
               "age": 30,
               "city": "New York",
               "skills": ["C++", "Java", "Python"]
           }
       )";
    std::istringstream iss(jsonStr);
    MySax sax_handler;
    bool success = nlohmann::json::sax_parse(iss, &sax_handler);
    if (!success) {
       std::cerr << "SAX parsing failed!" << std::endl;
    } else {
       std::cout << "SAX parsing successful!" << std::endl;
    }

}
//DOM解析
std::string jsonStr = R"(
           {
               "name": "John",
               "age": 30,
               "city": "New York",
               "skills": ["C++", "Java", "Python"]
           }
       )";

nlohmann::json j = nlohmann::json::parse(jsonStr);
//类型检查
nlohmann::json j = nlohmann::json::parse(jsonStr);
    if(j.contains("age") && j.at("age").is_number_integer()){
        int _age = j.at("age").get<int>();
        cout<<"age:"<<_age<<endl;
    }
//错误处理
    try {
        int _age = j.at("age").get<bool>();
        cout<<"age1:"<<_age<<endl;
    } catch (json::exception& e) {
        cout<<e.what()<<endl;
    }

二、nlohmann在C++中的应用

1、nlohmann的安装与引用

安装nlohmann/json.hpp非常简单,只需将json.hpp头文件复制到项目的适当位置即可。在C++源文件中,通过包含#include "nlohmann/json.hpp"来引用该库。

2、基本语法

//创建JSON对象
//可以使用nlohmann::json类来创建JSON对象,通过键值对的方式添加数据。
#include "nlohmann/json.hpp"  
  
int main() {  
    nlohmann::json j;  
    j["name"] = "John Doe";  
    j["age"] = 30;  
    j["is_student"] = false;  
    return 0;  
}


//解析JSON字符串
//nlohmann::json类还提供了从JSON字符串解析数据的功能。
int main() {  
    std::string json_str = R"({"name": "Jane Smith", "age": 25, "is_student": true})";  
    nlohmann::json j = nlohmann::json::parse(json_str);  
  
    std::cout << "Name: " << j["name"] << std::endl;  
    std::cout << "Age: " << j["age"] << std::endl;  
    std::cout << "Is Student: " << j["is_student"].get<bool>() << std::endl;  
    return 0;  
}

//生成JSON字符串
//可以使用dump()方法将nlohmann::json对象转换为JSON字符串。
int main() {  
    nlohmann::json j;  
    j["name"] = "Alice";  
    j["hobbies"] = nlohmann::json::array({ "reading", "painting", "swimming" });  
  
    std::string json_str = j.dump(4); // 4表示缩进空格数  
    std::cout << json_str << std::endl;  
  
    return 0;  
}

3、高级语法

//遍历JSON对象 
 nlohmann::json j = {  
        {"name", "Bob"},  
        {"age", 28},  
        {"address", {  
            {"street", "123 Main St"},  
            {"city", "New York"},  
            {"state", "NY"}  
        }}  

//结构化绑定
    for (const auto& [key, value] : j.items()) {  
        std::cout << "Key: " << key << ", Value: " << value << std::endl;  
    }  
//迭代器遍历
    for (auto it = j.begin(); it != j.end(); ++it) {
        std::cout << "Key: " << it.key() << ", Value: " << it.value() << std::endl;
    }
//遍历值
    for (const auto& value : j) {
        std::cout << "value: " << value<<std::endl;
    }    

//    for(const auto& [key, value] : j.items()) {
//        std::cout << "Key: " << key << ", Value: " << value << std::endl;
//        if (value.is_object()) {
//            for (const auto& [uuid, board] : value.items()) {
//                std::cout << "  Board UUID: " << uuid << std::endl;
//                for (const auto& [board_key, board_value] : board.items()) {
//                    std::cout << "    " << board_key << ": " << board_value << std::endl;
//                }
//            }
//        }
//    }

//    for (const auto& item : j.items()) {
//        std::cout << "Key: " << item.key() << ", Value: " << item.value() << std::endl;
//        // 如果value是另一个json对象,递归遍历它
//        if (item.value().is_object()) {
//            for (const auto& inner_item : item.value().items()) {
//                std::cout << "  Inner Key: " << inner_item.key() << ", Inner Value: " << inner_item.value() << std::endl;
//            }
//        }
//    }




//JSON数组操作
//nlohmann::json还支持JSON数组的操作,可以添加、删除和修改数组元素。
int main() {  
    nlohmann::json arr = nlohmann::json::array();  
    arr.push_back("Apple");  
    arr.push_back("Banana");  
    arr.push_back("Cherry");  
  
    arr[1] = "Blueberry"; // 修改数组中的元素  
    arr.pop_back(); // 删除数组中的最后一个元素  
    for (const auto& value : arr) {  
        std::cout << value << std::endl;  
    }  
    return 0;  
}
序列化和反序列化
#include "nlohmann/json.hpp"
class TestA
{
public:
    TestA(){}
    TestA(int v1, string v2):i(v1),s(v2) {}

    int i;
    string s;

void to_json(nlohmann::json& j) const {
             j = nlohmann::json{{"i", i}, {"s", s}};
         }
void from_json(const nlohmann::json& j) {
         i = j["i"];
         s = j["s"];
     }
//或者使用下面的宏来序列化和反序列化
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(TestA, i, s)
};

void main()
{

    std::string json_str = R"({
        "i": 1,
        "s": "str"
    })";
    json j1 = json::parse(json_str);
    TestA B;
    //反序列化
    B.from_json(j1);
    cout<<B.i<<B.s<<endl;
}

注意:

j.at("name").get_to(p.name) 和 p.name = j["name"] 都是将 JSON 对象中的指定字段值赋给结构体 xxx 中的成员变量 name。 然而,它们之间存在一些区别: 异常处理:j.at("name").get_to(p.name) 使用 at() 函数来获取指定字段的值,并将其赋给 p.name。如果 JSON 对象中不存在指定的字段,或者字段的值类型与 p.name 的类型不匹配,将抛出 json::out_of_range 或 json::type_error 异常。这使得您可以在出现错误时进行适当的异常处理。 容错性:p.name = j["name"] 使用索引运算符 [] 来获取指定字段的值,并将其赋给 p.name。如果 JSON 对象中不存在指定的字段,或者字段的值类型与 p.name 的类型不匹配,将引发未定义的行为。这意味着您需要确保 JSON 对象中存在指定的字段,并且字段的值类型与 p.name 的类型匹配,否则可能导致程序崩溃或产生不正确的结果。因此,如果您希望在 JSON 对象中不存在指定字段或字段类型不匹配时进行异常处理,并且更加安全和可靠,请使用 j.at("name").get_to(p.name)。如果您确定 JSON 对象中存在指定字段,并且字段的值类型与 p.name 的类型匹配,可以使用 p.name = j["name"] 来简化代码。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/6987.html
标签
评论
发布的文章

String转Json的几种方式

2024-05-09 11:05:04

iOS ------ JSONModel源码

2024-05-09 11:05:02

java去除 json 中的 \n, \t, \r

2024-05-09 11:05:57

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!