如何将任何值转换为对象并使用 boost::property_tree json 添加成员
Posted
技术标签:
【中文标题】如何将任何值转换为对象并使用 boost::property_tree json 添加成员【英文标题】:How to convert any value to an object and add members with boost::property_tree json 【发布时间】:2019-08-30 13:01:13 【问题描述】:我有一个程序可以在必要时修改 JSON 文档。程序必须将一个孩子添加到另一个值,无论它是否已经是一个对象。程序的行为应该是这样的:
-
如果键为“x”的对象不存在,则创建键为“x”的对象并将值 y 添加为子对象。
如果存在键为“x”的对象,请将值 y 设置为子对象。
如果键“x”存在并且是 ANY OTHER 类型,请将其删除,使用键“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?
【问题讨论】:
【参考方案1】:如果键“x”存在并且是 ANY OTHER 类型,则删除它,使用键“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);
失败 Live On Coliru
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);
将打印 Live On Coliru
更详细的注释
在清除值之前检查值是否存在似乎更有效:
if (x.get_value_optional<std::string>())
x.put_value("");
但由于属性树存储的字符串类型性质,没有区别,因为std::string
的条件始终为真。 (同样没有办法通过引用检索值。)
还要注意,在设置 n.prop1
嵌套属性时,如果您不控制源数据,您可能还必须检查 b
是否没有值,否则它会 fail again。
假设您的对象图结构是可合理预测的(甚至是静态的),我建议您提前完成:
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);
现在它甚至可以在没有任何预先存在的数据的情况下运行良好:
Live On Coliru
#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"
【讨论】:
以上是关于如何将任何值转换为对象并使用 boost::property_tree json 添加成员的主要内容,如果未能解决你的问题,请参考以下文章