C++中请求非const所有者修改const nlohmann::json键值的可行性
结论
该需求完全可以实现,不存在本质技术障碍,核心是先定位传入const子节点在根json对象中的位置,完成自定义合法性校验后,通过所有者持有的非const根对象完成修改即可。
实现思路
实现只需要解决三个核心问题:
- 校验传入的const子节点确实归属于待修改的根json对象,拦截外部传入无关json引用的非法修改请求
- 定位该子节点在根json中的访问路径
- 完成自定义合法性校验后,通过所有者持有的非const根对象修改对应路径的值
nlohmann::json原生支持RFC 6901定义的JSON Pointer规范,可以通过标准化路径直接定位到任意层级的子节点。我们只需要提前递归遍历根对象,通过内存地址匹配找到传入子节点对应的路径即可,全程不需要依赖危险的未定义操作。
完整可运行示例
#include <nlohmann/json.hpp> #include <iostream> #include <optional> #include <fstream> using json = nlohmann::json; // 辅助函数:递归查找目标const节点在根节点下对应的JSON Pointer std::optional<json::json_pointer> find_node_path(const json& root, const json& target) { // 匹配到当前节点就是目标,返回空路径(对应根节点自身) if (&root == &target) { return json::json_pointer{}; } // 对象类型遍历所有键递归查找 if (root.is_object()) { for (auto& [key, val] : root.items()) { auto sub_path = find_node_path(val, target); if (sub_path.has_value()) { return sub_path.value().push_front(key); } } } // 数组类型遍历所有索引递归查找 else if (root.is_array()) { for (size_t i = 0; i < root.size(); ++i) { auto sub_path = find_node_path(root[i], target); if (sub_path.has_value()) { return sub_path.value().push_front(i); } } } // 未找到目标节点返回空 return std::nullopt; } // 目标修改请求函数 template<typename T> bool RequestDataChange(json& root, const json& target_node, T new_val) { // 第一步:校验目标节点归属,拒绝不属于当前根对象的修改请求 auto path = find_node_path(root, target_node); if (!path.has_value()) { std::cerr << "非法修改请求:目标节点不属于当前json对象" << std::endl; return false; } // 第二步:自定义合法性校验逻辑,可根据业务需求调整规则 // 示例规则:仅允许修改根节点下的SomeKey字段 if (path.value().to_string() != "/SomeKey") { std::cerr << "校验失败:该路径无修改权限" << std::endl; return false; } // 第三步:校验通过,通过非const根对象和路径直接修改值 root[path.value()] = new_val; return true; } int main() { // 对应原示例中从文件加载json的逻辑 std::ifstream json_file("someJsonFile.json"); json j = json::parse(json_file); const json& constJsonRef = j; // 发起修改请求 bool ret = RequestDataChange(j, constJsonRef["SomeKey"], 6); if (ret) { std::cout << "修改成功,修改后j的值为:" << j.dump(2) << std::endl; } // 非法路径的修改请求会被直接拦截 ret = RequestDataChange(j, constJsonRef["OtherKey"], "invalid_val"); if (!ret) { std::cout << "非法修改请求已被拦截" << std::endl; } return 0; }
注意事项
- 递归查找路径的时间复杂度和json节点总数正相关,如果你的json结构体量极大、修改请求触发频率很高,可以提前缓存所有节点地址和对应路径的映射,降低性能开销
- 不要直接对传入的const子节点用
const_cast转非const后修改,必须先完成节点归属校验,否则如果外部传入的是其他const json对象的引用,强制转非const修改会触发未定义行为 - 如果你的业务场景允许调用方直接传JSON Pointer字符串(比如
"/SomeKey")而非子节点引用,可以直接省掉递归查找路径的步骤,执行效率会更高
内容的提问来源于stack exchange,提问作者Omer Kawaz
相关产品推荐
相关产品推荐

