[simdjson] ondemand::value | object array
第四章:值类型
欢迎回来!
在前几章中,我们了解到解析器是处理JSON的工具,我们使用必要的填充字符串格式准备数据,调用parser.iterate()
会返回一个文档对象,这是我们在按需API中处理JSON数据的起点。
JSON文档结构由不同元素组成:对象{}
、数组[]
、字符串"类似这样"
、数字123
或1.2
、布尔值true
/false
以及null
。
当从根文档导航进入文档,或在数组/对象内部移动时,我们需要一种表示这些可能JSON元素的方法。
这就是simdjson::ondemand::value
概念的由来。
什么是值类型?
在simdjson按需API中,simdjson::ondemand::value
是一个临时句柄,表示在导航过程中发现的特定JSON元素。
它类似于指针或光标,当前高亮显示JSON数据中的某个项目。
关键点在于,value
对象并不拥有其表示数据的任何内存。
它只是一个轻量级视图或句柄,指向:
- 原始填充的JSON缓冲区
- 解析器的内部状态(如结构索引或"磁带")
可用以下示意图表示:
value
对象是我们在按需处理JSON数据时的主要交互方式。
我们通过其方法来判断JSON元素类型(例如是字符串?对象?数字?),并提取实际内容(获取字符串字符、数字值,或获取导航对象/数组的句柄)。
获取值类型
在遍历JSON结构时,可以通过以下方式获取simdjson::ondemand::value
对象:
1. 从数组获取
遍历数组时,循环中的每个元素都是value
#include <simdjson.h>int main() {simdjson::ondemand::parser parser;simdjson::padded_string json_data = R"([10, "hello", true, null])"_padded;auto doc = parser.iterate(json_data); // doc是文档对象// 假设doc是数组,获取数组句柄auto array = doc.get_array();// 遍历数组:每个'element'都是simdjson::ondemand::valuefor (auto element_result : array) {if (element_result.error()) { /* 处理错误 */ break; }simdjson::ondemand::value element = element_result.value();// 'element'现在代表数组中的元素(10、"hello"、true或null)// ... 使用'element' ...}return 0;}
2. 从对象获取
访问对象字段时,获取的结果是value
类型:
#include <simdjson.h>int main()
{simdjson::ondemand::parser parser;auto json_data = R"({"name": "simdjson", "version": 3})"_padded;auto doc = parser.iterate(json_data);auto object = doc.get_object();auto name_value = object["name"]; // 获取字符串值auto version_value = object["version"]; // 获取数字值return 0;
}
3. 从文档根获取(标量类型)
当根元素是简单标量时,可直接从文档获取value
:
#include <simdjson.h>int main()
{simdjson::ondemand::parser parser;auto json_data = R"("字符串根元素")"_padded;auto doc = parser.iterate(json_data);simdjson::ondemand::value root_value = doc; // 直接赋值return 0;
}
使用值类型:类型判断与内容提取
获取value
后主要进行两个操作:
类型判断
通过type()
方法判断元素类型:
auto type_result = element.type();
if (type_result.error()) break;
auto element_type = type_result.value();switch(element_type) {case json_type::number: // 处理数字case json_type::string: // 处理字符串case json_type::boolean: // 处理布尔值// 其他类型处理...
}
内容提取
根据类型调用对应方法提取数据:
switch(element_type) {case json_type::number: {int64_t num = element.get_int64(); // 提取整型break;}case json_type::string: {std::string_view str = element.get_string(); // 提取字符串视图break;}// 其他类型提取方法...
}
生命周期与临时性
value
对象具有临时性特征:
- 调用
get_...()
方法后,该值即被消费,原value
失效 - 父容器迭代或超出作用域时,相关
value
也会失效
生命周期示意图:
最佳实践:
- 获取
value
后立即提取数据 - 长期存储应保存提取后的数据(如字符串、数字),而非
value
本身
底层原理
value
本质上是value_iterator
的包装器,包含指向原始JSON数据和解析器磁带状态的指针:
class value {value_iterator iter; // 包含JSON指针和解析状态// 相关操作方法...
};
当调用
type()
时,迭代器仅检查当前标记类型;调用get_...()
时会:
验证
标记类型匹配解析
标记内容推进
迭代器位置
总结
value
是表示单个JSON元素的临时句柄- 通过数组迭代、对象字段访问或文档根获取
- 使用
type()
判断类型,get_...()
提取内容 - 具有临时性特征,需及时处理数据
- 底层依赖解析器状态和原始数据缓冲区
下一章:对象与数组
第五章:object与array
在前几章中,我们掌握了解析器的核心作用,学习了如何用填充字符串准备JSON数据,以及如何通过parser.iterate()
获取表示JSON根节点的文档对象(本质上是特殊的值类型)。
我们还了解到simdjson::ondemand::value
是表示任意JSON元素的通用句柄。
JSON文档的结构化特性由JSON对象({}
)和JSON数组([]
)实现。当遇到表示对象或数组的值类型时,我们需要进入其内部结构进行访问。
这正是simdjson::ondemand::object
和simdjson::ondemand::array
的设计目标。它们是用于导航JSON嵌套结构的专用句柄。
对象与数组句柄的定义
在simdjson按需API中:
simdjson::ondemand::object
是处理JSON对象({}
)的句柄,支持按键查找字段或遍历所有键值对simdjson::ondemand::array
是处理JSON数组([]
)的句柄,支持遍历元素或按索引访问
与值类型类似,这些句柄不持有内存,它们是与原始填充字符串和解析器内部状态绑定的视图。
获取句柄
当值类型表示对象或数组时,可以获取对应句柄。若JSON根节点是对象或数组,可直接从文档获取初始句柄:
#include <simdjson.h>// 处理根对象
auto doc_obj = parser.iterate(R"({"message": "hello"})"_padded);
auto root_object = doc_obj.get_object();// 处理根数组
auto doc_array = parser.iterate(R"([1,2,3])"_padded);
auto root_array = doc_array.get_array();
对于嵌套结构的访问示例:
auto json = R"({"user": {"name": "Alice", "tags": ["simdjson", "cpp"]}})"_padded;
auto doc = parser.iterate(json);// 逐层获取句柄
auto root_obj = doc.get_object();
auto user_val = root_obj["user"]; // 获取用户对象值
auto user_obj = user_val.get_object(); // 转换为对象句柄
auto tags_val = user_obj["tags"]; // 获取标签数组值
auto tags_array = tags_val.get_array();// 转换为数组句柄
对象句柄操作
获取object
句柄后,可通过两种方式访问字段:
按键访问
auto user_obj = doc.get_object();
auto name = user_obj["name"].get_string(); // 获取字符串
auto age = user_obj["age"].get_int64(); // 获取整型
auto is_student = user_obj["isStudent"].get_bool(); // 链式调用// 处理不存在字段
if (auto city = user_obj["city"]; city.error())
{std::cerr << "字段不存在: " << city.error();
}
遍历字段
for (auto field : user_obj)
{auto key = field.key(); // 获取字段名auto value = field.value(); // 获取值句柄switch(value.type()) {case json_type::string: std::cout << key << ": " << value.get_string();break;// 其他类型处理...}
}
数组句柄操作
遍历元素
auto numbers = doc.get_array();
for (auto element : numbers)
{if (element.type() == json_type::number) {std::cout << element.get_int64() << "\n";}
}
按索引访问
auto arr = doc.get_array();
auto second = arr.at(1).get_string(); // 访问索引1元素
auto tenth = arr.at(10); // 越界访问将报错
生命周期管理
关键注意事项通过序列图展示:
实现原理
对象和数组句柄本质上是迭代器的包装:
// 简化的对象句柄实现
class object
{value_iterator iter; // 内部迭代器value operator[](string_view key) {iter.find(key); // 定位键值return value(iter.child());}
};// 简化的数组迭代器
class array_iterator
{value_iterator iter;value operator*() {return value(iter);}void operator++() {iter.step(); // 推进迭代}
};
总结
对象句柄
支持按键查找和遍历字段数组句柄
支持遍历和索引访问(注意O(N)复杂度)- 所有句柄均为临时视图,需及时处理数据
- 操作会消耗迭代位置,禁止重复访问
- 错误处理需检查每个
simdjson_result
下一章:错误处理