如何将任何值转换为对象并使用 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 添加成员的主要内容,如果未能解决你的问题,请参考以下文章

如何将整数变量转换为 QTime 对象

如何将 created_at 字段值从 Instagram 媒体对象转换为日期

如何将对象转换为数组并保存对象中的 id

Delphi:如何将表结构转换为对象

如何在 javafx 中将任何文本或字母转换为图像

如何解析对象内的 JSON 字典