In modern C++ application development, exchanging data with external systems or persisting complex data structures is commonplace. JSON and XML, as two of the most popular data exchange formats, play a crucial role. Although the C++ standard library provides basic file I/O functionality, it does not include native support for JSON or XML. Therefore, leveraging efficient and user-friendly third-party libraries has become the best choice. This article will introduce two highly regarded C++ libraries: RapidJSON for JSON parsing and pugixml for XML parsing.
1. Why Choose Third-party Libraries?
Before diving into the details, an obvious question arises: why not parse manually? The answer lies in complexity, efficiency, and robustness. Manually writing a parser to handle complex, deeply nested JSON or XML files is not only tedious but also prone to errors. Libraries like RapidJSON and pugixml have been rigorously tested and share the following common advantages:
- High Performance: One of their design goals is extreme parsing and serialization speed.
- Ease of Use: They provide intuitive APIs that allow developers to easily access and modify data.
- Lightweight: Typically, only a few header files need to be included (i.e., header-only or minimal compilation), making integration very simple.
- Robustness: They handle various edge cases and format errors well.
2. RapidJSON: A Powerful Tool for JSON Handling
RapidJSON is a fast JSON parser/generator for C++, supporting both SAX and DOM style APIs.
Integration and Installation
RapidJSON is a header-only library. Simply download the <span>include/rapidjson</span> directory from its GitHub repository and add it to your project’s header file search path.
git clone https://github.com/Tencent/rapidjson.git
# Then add the rapidjson/include path to your compiler
Core Usage: DOM API
The DOM (Document Object Model) API parses the entire JSON document into a tree structure in memory, facilitating random access and modification.
Example: Parsing a JSON String and Reading Values
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include <iostream>
#include <string>
using namespace rapidjson;
int main() {
// 1. Prepare a JSON string
const char* json = R"(
{
"name": "Alice",
"age": 30,
"is_student": false,
"courses": ["C++", "Data Structures"]
}
)";
// 2. Parse the string into Document
Document document;
document.Parse(json);
// 3. Check if parsing was successful and the root node is of Object type
if (document.IsObject()) {
// 4. Use HasMember() and [] to access data
if (document.HasMember("name") && document["name"].IsString()) {
std::string name = document["name"].GetString();
std::cout << "Name: " << name << std::endl;
}
if (document.HasMember("age") && document["age"].IsInt()) {
int age = document["age"].GetInt();
std::cout << "Age: " << age << std::endl;
}
// 5. Accessing arrays
if (document.HasMember("courses") && document["courses"].IsArray()) {
const Value& courses = document["courses"];
std::cout << "Courses: ";
for (SizeType i = 0; i < courses.Size(); i++) {
std::cout << courses[i].GetString() << " ";
}
std::cout << std::endl;
}
}
return 0;
}
Example: Creating and Generating JSON
// ... (Include headers as above)
int main() {
Document document;
document.SetObject(); // Set the document to Object type
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
// Add key-value pairs
Value name_val;
name_val.SetString("Bob", allocator);
document.AddMember("name", name_val, allocator);
document.AddMember("score", 95.5, allocator);
// Create an array
Value arr(kArrayType);
arr.PushBack("Reading", allocator).PushBack("Swimming", allocator);
document.AddMember("hobbies", arr, allocator);
// Convert Document to string
StringBuffer buffer;
Writer writer(buffer);
document.Accept(writer);
std::string json_str = buffer.GetString();
std::cout << "Generated JSON: " << json_str << std::endl;
// Output: {"name":"Bob","score":95.5,"hobbies":["Reading","Swimming"]}
return 0;
}
Important Note: RapidJSON uses an “allocator” to manage memory, and all operations that require allocating strings or complex data must pass this allocator.
3. pugixml: A Lightweight and Efficient XML Processor
pugixml is a lightweight, simple, and efficient C++ XML processing library that supports XPath 1.0 queries.
Integration and Installation
pugixml is also very easy to integrate. Download the source files from the pugixml official website or GitHub. Typically, you only need to add <span>src/pugixml.cpp</span> to your project and include the <span>src/pugixml.hpp</span> header file.
Core Usage
Example: Loading and Parsing an XML File
Assuming we have a <span>config.xml</span> file:
<?xml version="1.0"?>
<config>
<log level="info" path="/var/log/app.log"/>
<server ip="192.168.1.100" port="8080"/>
<users>
<user id="1">Alice</user>
<user id="2">Bob</user>
</users>
</config>
#include "pugixml.hpp"
#include <iostream>
int main() {
pugi::xml_document doc;
// Load XML file
pugi::xml_parse_result result = doc.load_file("config.xml");
// Check load result
if (!result) {
std::cerr << "XML parsed with errors: " << result.description() << std::endl;
return -1;
}
// 1. Node Traversal
std::cout << "--- Node Traversal ---" << std::endl;
pugi::xml_node config = doc.child("config");
pugi::xml_node log = config.child("log");
pugi::xml_node server = config.child("server");
// Read node attributes
std::cout << "Log Level: " << log.attribute("level").as_string() << std::endl;
std::cout << "Server IP: " << server.attribute("ip").as_string()
<< ":" << server.attribute("port").as_int() << std::endl;
// Traverse child nodes
pugi::xml_node users = config.child("users");
for (pugi::xml_node user : users.children("user")) {
std::cout << "User ID: " << user.attribute("id").as_int()
<< ", Name: " << user.text().as_string() << std::endl;
}
// 2. Using XPath Queries (more powerful and flexible)
std::cout << "\n--- XPath Query ---" << std::endl;
pugi::xpath_node_set xpath_users = doc.select_nodes("/config/users/user");
for (pugi::xpath_node node : xpath_users) {
pugi::xml_node user = node.node();
std::cout << "User (XPath): " << user.text().as_string() << std::endl;
}
// Find nodes with specific attributes
pugi::xpath_node log_node = doc.select_node("//log[@level='info']");
if (log_node) {
std::cout << "Found log path: " << log_node.node().attribute("path").as_string() << std::endl;
}
return 0;
}
Example: Creating and Saving XML
#include "pugixml.hpp"
int main() {
pugi::xml_document doc;
// Add declaration node <?xml ...?>
auto declaration = doc.append_child(pugi::node_declaration);
declaration.append_attribute("version") = "1.0";
declaration.append_attribute("encoding") = "UTF-8";
// Create root node <root>
auto root = doc.append_child("root");
// Add child nodes and attributes to root node <item id="1">Hello</item>
auto item = root.append_child("item");
item.append_attribute("id") = 1;
item.text() = "Hello";
auto another_item = root.append_child("item");
another_item.append_attribute("id") = 2;
another_item.text() = "World";
// Save document to file
doc.save_file("output.xml", " "); // The second parameter is indentation for beautifying output
return 0;
}
4. Summary and Selection
| Feature | RapidJSON (JSON) | pugixml (XML) |
|---|---|---|
| Core Advantages | Extremely fast, memory-friendly | Simple and intuitive API, supports XPath |
| Integration Method | Header-only | Usually requires compiling a <span>.cpp</span> file |
| API Style | DOM & SAX | DOM & SAX (via parser) |
| Data Access | Through <span>[]</span> and <span>GetXXX()</span> |
Through node/attribute objects and <span>.child()</span>, <span>.attribute()</span> |
| Query Capability | Requires manual traversal | Powerful XPath 1.0 support |
- When to Choose RapidJSON: When you need to handle JSON format and have extremely high performance requirements, it is undoubtedly the first choice. Especially in scenarios like network transmission, game development, or high-frequency logging.
- When to Choose pugixml: When you need to handle configuration files, legacy system interfaces, or any XML-based data sources. Its XPath support makes extracting data from complex XML exceptionally easy.
With these two lightweight yet powerful libraries, C++ developers can seamlessly integrate JSON and XML parsing and serialization into their projects, efficiently completing data processing tasks.