c++ - 如何将任何值转换为对象并使用 boost::property_tree json 添加成员

标签 c++ json boost boost-propertytree

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

  1. 如果键为“x”的对象不存在,则创建键为“x”的对象并添加值为 y 作为子对象。
  2. 如果键为“x”的对象确实存在,则将值 y 设置为子对象。
  3. 如果键“x”存在并且是任何其他类型,则删除它,使用键“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!"
    }
}

Pass 'Converting value test'
Could not make create json: <unspecified file>: ptree contains data that cannot be represented in JSON format

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

如何实现上面列表中的场景 3?

最佳答案

If the key "x" exists and is ANY OTHER type, delete it, create an object with the key "x" and then add value y as a child. Also, they don't observe any of the JSON data types.

你的计划注定要失败。 Property Tree 不是 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("");
}

但是由于 Property Tree 存储的字符串类型性质,没有区别,因为条件对于 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"
        }
    }
}

关于c++ - 如何将任何值转换为对象并使用 boost::property_tree json 添加成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57727264/

相关文章:

c++ - 单词比较 - Vectors/Getopt

c++ - 在动态数组中使用指针时出现问题

node.js - Sequelize : How to match atleast 1 value from array stored in db with array in the request

c++ - boost local_date_time 中没有 setter 方法?

c++ - 如何创建简单的 Boost.Build make 文件,其中包含指向我在 Windows 和 Linux 上使用的库的链接?

c++ - Visual Studio 不会更新 C++ 版本

c++ - 如何静态链接Qt库?

ios - 如何从 Swift4 中的嵌套字典访问值?

json - 有没有办法让 json.Unmarshal() 根据 "type"属性选择结构类型?

c++ - boost managed_mapped_file : setting maximum allowed memory usage