如何将任何值转换为对象并使用 boost::p roperty_tree json 添加成员

How to convert any value to an object and add members with boost::property_tree json

本文关键字:roperty tree 成员 添加 json boost 任何值 转换 对象      更新时间:2023-10-16

>我有一个程序,可以在必要时修改JSON文档。程序必须将子项添加到另一个值,无论它是否已经是一个对象。程序的行为应如下所示:

如果键为"x"的对象
  1. 不存在,则创建键为"x"的对象,并将值 y 作为子项添加。
  2. 如果键为"x"的对象确实存在,请将值 y 设置为子项。
  3. 如果键"x
  4. "存在并且是任何其他类型,请将其删除,使用键"x"创建一个对象,然后将值y添加为子项。

我看到了测试属性树值是否存在或它们是否是指定类型的方法,但没有方法可以测试它是否是对象。

这是我制作的一个简单的程序来说明我的意思:

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <sstream>
#include <iostream>
const char *json = "{"
""object" : { "mighty" : "wind" },"
""boolean" : true"
"}";
void printTree( std::string name, boost::property_tree::ptree tree )
{
std::cout << "Pass '" << name << "'" << std::endl;
try
{
std::stringstream ss;
boost::property_tree::write_json( ss, tree );
std::cout << ss.str() << std::endl;
}
catch( std::exception &e )
{
std::cout << "Could not make create json: " << e.what() << std::endl;
}
}
int main( int argc, char *argv[] )
{
boost::property_tree::ptree tree;
// Load it
std::istringstream ss_json( json );
boost::property_tree::read_json( ss_json, tree );
// Add a value to an object that doesn't exist
tree.put( "none.value", "hello!" );
// Print to see
printTree( "Nonexistent value test", tree );
// Add a value to the object
tree.put( "object.value", "bello!" );
// Print this one
printTree( "Adding value test", tree );
// Convert boolean to an object and add a value
tree.put( "boolean.value", "mello!" );
// Print it
printTree( "Converting value test", tree );
}

输出将是:

Pass 'Nonexistent value test'
{
"object": {
"mighty": "wind"
},
"boolean": "true",
"none": {
"value": "hello!"
}
}
Pass 'Adding value test'
{
"object": {
"mighty": "wind",
"value": "bello!"
},
"boolean": "true",
"none": {
"value": "hello!"
}
}

通过"转换值测试">
无法创建 json:<未指定的文件>:ptree 包含无法以 JSON 格式表示的数据

您可以在输出中看到,最后一步无法转换为 JSON(当我尝试设置它时不会抛出(。

如何实现上述列表中的方案 3?

如果键"x"存在并且是任何其他类型,请将其删除,使用键"x"创建一个对象,然后将值y添加为子项。此外,它们不会观察到任何 JSON 数据类型。

你的计划注定要失败。属性树不是 JSON 库。属性树可以在同一节点上具有数据和子节点。例如

ptree p;
auto& x = p.put_child("x", {});
x.put_value("hello");
write_json(std::cout, p);

指纹

{
"x": "hello"
}

但是添加

/*auto& a = */ p.put_child("x.a", {});
write_json(std::cout, p);

科里鲁直播时失败

terminate called after throwing an instance of 'boost::wrapexcept<boost::property_tree::json_parser::json_parser_error>'
what():  <unspecified file>: ptree contains data that cannot be represented in JSON format

解决方法是在添加属性之前或添加属性时删除任何值:

x.put_value("");
auto& a = p.put_child("x.a", {});
a.add("prop1", 123);
a.add("prop2", "one two three");
a.add("b.prop1", "nesting");
write_json(std::cout, p);

会打印在科里鲁上直播

更精细的笔记

在清除值之前检查值是否存在似乎更有效:

if (x.get_value_optional<std::string>()) {
x.put_value("");
}

但是由于属性树存储的字符串类型性质,没有区别,因为条件对于std::string总是正确的。(同样,无法通过引用检索值。

另请注意,在设置n.prop1嵌套属性时,如果不控制源数据,您可能还必须检查b是否没有值,否则它会再次失败。

假设你的对象图结构是合理可预测的(甚至是静态的(,我建议提前结束它:

for (auto key : { "x", "x.a", "x.a.b" }) {
if (auto child = p.get_child_optional(key)) {
std::cout << "clearing " << key << std::endl;
child->put_value("");
}
}

可以用一个助手来概括:

clear_values("x.a.b", p);

可以实现为

void clear_values(ptree::path_type path, ptree& p) {
if (path.empty())
return;
auto head = path.reduce();
auto child = p.get_child_optional(head);
if (child) {
child->put_value("");
clear_values(path, *child);
}
}

奖金

事实上,有了这样的帮助程序,也可以动态创建预期的层次结构:

void clear_values(ptree::path_type path, ptree& p, bool create = false) {
if (path.empty())
return;
auto head = path.reduce();
auto child = p.get_child_optional(head);
if (!child && create) {
child = p.put_child(head, {});
}
if (child) {
child->put_value("");
clear_values(path, *child, create);
}
}

现在,它甚至可以在没有任何预先存在的数据的情况下正常工作:

住在科里鲁

#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
void clear_values(ptree::path_type path, ptree& p, bool create = false) {
if (path.empty())
return;
auto head = path.reduce();
auto child = p.get_child_optional(head);
if (!child && create) {
child = p.put_child(head, {});
}
if (child) {
child->put_value("");
clear_values(path, *child, create);
}
}
int main() {
ptree p;
clear_values("x.a.b", p, true);
auto& a = p.get_child("x.a");
a.add("prop1", 123);
a.add("prop2", "one two three");
a.add("b.prop1", "nesting");
write_json(std::cout, p);
}

指纹

{
"x": {
"a": {
"b": {
"prop1": "nesting"
},
"prop1": "123",
"prop2": "one two three"
}
}
}