为什么 Boost 属性树 write_json 将所有内容保存为字符串?有可能改变这一点吗?

发布于 2024-09-01 22:47:19 字数 1035 浏览 4 评论 0 原文

我正在尝试使用 boost 属性树 write_json 进行序列化,它将所有内容保存为字符串,并不是数据错误,而是我每次都需要显式地转换它们,并且我想在其他地方使用它们。 (就像在 python 或其他 C++ json(非 boost)库中一样)

这里是一些示例代码以及我根据语言环境得到的内容:

boost::property_tree::ptree root, arr, elem1, elem2;
elem1.put<int>("key0", 0);
elem1.put<bool>("key1", true);
elem2.put<float>("key2", 2.2f);
elem2.put<double>("key3", 3.3);
arr.push_back( std::make_pair("", elem1) );
arr.push_back( std::make_pair("", elem2) );
root.put_child("path1.path2", arr);

std::stringstream ss;
write_json(ss, root);
std::string my_string_to_send_somewhare_else = ss.str();

并且 my_string_to_send_somewhere_else 是某物。像这样:

{
    "path1" :
    {
       "path2" :
       [
            {
                 "key0" : "0",
                 "key1" : "true"
            },
            {
                 "key2" : "2.2",
                 "key3" : "3.3"
            }
       ]
    }
}

有没有办法将它们保存为值,例如: “key1”:true还是“key2”:2.2

I'm trying to serialize using boost property tree write_json, it saves everything as strings, it's not that data are wrong, but I need to cast them explicitly every time and I want to use them somewhere else. (like in python or other C++ json (non boost) library)

here is some sample code and what I get depending on locale:

boost::property_tree::ptree root, arr, elem1, elem2;
elem1.put<int>("key0", 0);
elem1.put<bool>("key1", true);
elem2.put<float>("key2", 2.2f);
elem2.put<double>("key3", 3.3);
arr.push_back( std::make_pair("", elem1) );
arr.push_back( std::make_pair("", elem2) );
root.put_child("path1.path2", arr);

std::stringstream ss;
write_json(ss, root);
std::string my_string_to_send_somewhare_else = ss.str();

and my_string_to_send_somewhere_else is sth. like this:

{
    "path1" :
    {
       "path2" :
       [
            {
                 "key0" : "0",
                 "key1" : "true"
            },
            {
                 "key2" : "2.2",
                 "key3" : "3.3"
            }
       ]
    }
}

Is there anyway to save them as the values, like:
"key1" : true or "key2" : 2.2?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

面如桃花 2024-09-08 22:47:19

好吧,我已经像这样解决了它,(当然它并不适合所有人,因为它有点黑客,需要进一步的工作)。


我编写了自己的 write_json 函数(只需将文件 json_parser.hppjson_parser_write.hpp 复制到我的项目中)并修改以下内容json_parser_write.hpp 中的行:

  1. 注释第 37 行 - 转义引号 '"'
  2. 更改第 76 行 - 这样就不再添加引号:
    流<< Ch('"') << 数据 << Ch('"'); ==>流<< data;

那么除了字符串之外的值将被正确保存,所以我为它编写了自定义翻译器:

template <typename T>
struct my_id_translator
{
    typedef T internal_type;
    typedef T external_type;

    boost::optional<T> get_value(const T &v) { return  v.substr(1, v.size() - 2) ; }
    boost::optional<T> put_value(const T &v) { return '"' + v +'"'; }
};

并简单地使用以下方法保存了字符串:

elem2.put<std::string>("key2", "asdf", my_id_translator<std::string>());

完整的程序:

#include <iostream>
#include <string>
#include <sstream>

#include <boost/property_tree/ptree.hpp>

#include "property_tree/json_parser.hpp" // copied the headers

template <typename T>

struct my_id_translator
{
    typedef T internal_type;
    typedef T external_type;

    boost::optional<T> get_value(const T &v) { return  v.substr(1, v.size() - 2) ; }
    boost::optional<T> put_value(const T &v) { return '"' + v +'"'; }
};

int main(int, char *[])
{
    using namespace std;
    using boost::property_tree::ptree;
    using boost::property_tree::basic_ptree;
    try
    {
        ptree root, arr,elem2;
        basic_ptree<std::string, std::string> elem1;
        elem1.put<int>("int", 10 );
        elem1.put<bool>("bool", true);
        elem2.put<double>("double", 2.2);
        elem2.put<std::string>("string", "some string", my_id_translator<std::string>());

        arr.push_back( std::make_pair("", elem1) );
        arr.push_back( std::make_pair("", elem2) );
        root.put_child("path1.path2", arr);

        std::stringstream ss;
        write_json(ss, root);
        std::string my_string_to_send_somewhere_else = ss.str();

        cout << my_string_to_send_somewhere_else << endl;

    }
    catch (std::exception & e)
    {
        cout << e.what();
    }
    return 0;
}

结果:)

{
    "path1":
    {
        "path2":
        [
            {
                "int": 10,
                "bool": true
            },
            {
                "double": 2.2,
                "string": "some string"
            }
        ]
    }
}

Ok, I've solved it like this, (of course it won't suite for everybody, as it is a bit of a hack, that need further work).


I've wrote my own write_json function (simply copied the files, json_parser.hpp and json_parser_write.hpp to my project) and modified the following lines in json_parser_write.hpp:

  1. commented line 37 - escaping the quote '"'
  2. changed line 76 - so that it doesn't add quotes anymore:
    stream << Ch('"') << data << Ch('"'); ==> stream << data;

Then values will be saved properly except for strings, so I wrote custom translator for it:

template <typename T>
struct my_id_translator
{
    typedef T internal_type;
    typedef T external_type;

    boost::optional<T> get_value(const T &v) { return  v.substr(1, v.size() - 2) ; }
    boost::optional<T> put_value(const T &v) { return '"' + v +'"'; }
};

and simply saved string using:

elem2.put<std::string>("key2", "asdf", my_id_translator<std::string>());

complete program:

#include <iostream>
#include <string>
#include <sstream>

#include <boost/property_tree/ptree.hpp>

#include "property_tree/json_parser.hpp" // copied the headers

template <typename T>

struct my_id_translator
{
    typedef T internal_type;
    typedef T external_type;

    boost::optional<T> get_value(const T &v) { return  v.substr(1, v.size() - 2) ; }
    boost::optional<T> put_value(const T &v) { return '"' + v +'"'; }
};

int main(int, char *[])
{
    using namespace std;
    using boost::property_tree::ptree;
    using boost::property_tree::basic_ptree;
    try
    {
        ptree root, arr,elem2;
        basic_ptree<std::string, std::string> elem1;
        elem1.put<int>("int", 10 );
        elem1.put<bool>("bool", true);
        elem2.put<double>("double", 2.2);
        elem2.put<std::string>("string", "some string", my_id_translator<std::string>());

        arr.push_back( std::make_pair("", elem1) );
        arr.push_back( std::make_pair("", elem2) );
        root.put_child("path1.path2", arr);

        std::stringstream ss;
        write_json(ss, root);
        std::string my_string_to_send_somewhere_else = ss.str();

        cout << my_string_to_send_somewhere_else << endl;

    }
    catch (std::exception & e)
    {
        cout << e.what();
    }
    return 0;
}

result :)

{
    "path1":
    {
        "path2":
        [
            {
                "int": 10,
                "bool": true
            },
            {
                "double": 2.2,
                "string": "some string"
            }
        ]
    }
}
水中月 2024-09-08 22:47:19

Boost 确认其实现并非 100% 符合 JSON 标准。检查以下链接以查看他们的解释:
制作一个保留 JSON 类型的 ptree 变体是未来的计划,但还很遥远。

Boost confirms its implementation has no 100% conformance to JSON standard. Check the following link to see their explanation:
Making a ptree variant that preserves JSON types is a future plan, but far off.!

独夜无伴 2024-09-08 22:47:19

我最终在我的实用程序中添加了另一个函数来解决这个问题:

#include <string>
#include <regex>
#include <boost/property_tree/json_parser.hpp>

namespace bpt = boost::property_tree;
typedef bpt::ptree JSON;
namespace boost { namespace property_tree {
    inline void write_jsonEx(const std::string & path, const JSON & ptree)
    {
        std::ostringstream oss;
        bpt::write_json(oss, ptree);
        std::regex reg("\\\"([0-9]+\\.{0,1}[0-9]*)\\\"");
        std::string result = std::regex_replace(oss.str(), reg, "$1");

        std::ofstream file;
        file.open(path);
        file << result;
        file.close();
    }
} }

希望有帮助。

I ended up adding another function to my utils to solve this:

#include <string>
#include <regex>
#include <boost/property_tree/json_parser.hpp>

namespace bpt = boost::property_tree;
typedef bpt::ptree JSON;
namespace boost { namespace property_tree {
    inline void write_jsonEx(const std::string & path, const JSON & ptree)
    {
        std::ostringstream oss;
        bpt::write_json(oss, ptree);
        std::regex reg("\\\"([0-9]+\\.{0,1}[0-9]*)\\\"");
        std::string result = std::regex_replace(oss.str(), reg, "$1");

        std::ofstream file;
        file.open(path);
        file << result;
        file.close();
    }
} }

Hope that helps.

独夜无伴 2024-09-08 22:47:19

我能想到的最简单、最干净的解决方案是生成带有占位符的 JSON,并在最后用实际值替换字符串,从而放弃额外的引号。

static string buildGetOrdersCommand() {
    ptree root;
    ptree element;
    element.put<string>("pendingOnly", ":pendingOnly");
    element.put<string>("someIntValue", ":someIntValue");

    root.put("command", "getOrders");
    root.put_child("arguments", element);

    std::ostringstream buf;
    write_json(buf, root, false);
    buf << std::endl;

    string json = buf.str();
    replace(json, ":pendingOnly", "true");
    replace(json, ":someIntValue", std::to_string(15));

    return json;
}

static void replace(string& json, const string& placeholder, const string& value) {
    boost::replace_all<string>(json, "\"" + placeholder + "\"", value);
}

结果是

{"command":"getOrders","arguments":{"pendingOnly":true,"someIntValue":15}}

The simplest and cleanest solution that i could come up with was generating the JSON with placeholders and in the end string replacing with the actual value ditching the extra quotes.

static string buildGetOrdersCommand() {
    ptree root;
    ptree element;
    element.put<string>("pendingOnly", ":pendingOnly");
    element.put<string>("someIntValue", ":someIntValue");

    root.put("command", "getOrders");
    root.put_child("arguments", element);

    std::ostringstream buf;
    write_json(buf, root, false);
    buf << std::endl;

    string json = buf.str();
    replace(json, ":pendingOnly", "true");
    replace(json, ":someIntValue", std::to_string(15));

    return json;
}

static void replace(string& json, const string& placeholder, const string& value) {
    boost::replace_all<string>(json, "\"" + placeholder + "\"", value);
}

And the result is

{"command":"getOrders","arguments":{"pendingOnly":true,"someIntValue":15}}

破晓 2024-09-08 22:47:19

因为我们有 typedef basic_ptree;树;在 boost 库中,boost 始终将每个值序列化为字符串,并将所有值解析为等效的字符串。

As we have typedef basic_ptree<std::string, std::string> ptree; in the boost libraries, boost will always serialize each value as string and parse all values to a string equivalent.

迷爱 2024-09-08 22:47:19

从输出的 JSON 可以清楚地看出,序列化程序使用某种 .toString() 方法将所有内容序列化为字符串 - 也就是说,它不知道每个成员的类型,因此将所有内容都包含在“”中。

有关详细信息,请参阅使用属性树在 Boost 中创建 JSON 数组这个问题。

From the outputted JSON it is clear that the serializer serializes everything to strings using some sort of .toString() method - that is, its unaware of the type of each member and so encloses everything in " ".

See Creating JSON arrays in Boost using Property Trees for more about this problem .

心碎无痕… 2024-09-08 22:47:19

所有需要显式自定义字符串翻译器的解决方案对我来说似乎很容易出错,因为有时可能会忘记它。最好通过继承为属性树的 put 方法提供某种重载方式来隐式处理此问题,但这不可能以稳健的方式进行,因为它是模板,并且您必须确保树的所有方法的完全协变。如果可能的话,一般情况下也应该避免改变 boost 库的内容作为解决方法。

到目前为止,我发现的最可靠的无黑客方法是(自 C++11 起):

  • 使用带有 > 的 boost-property 树
  • 提供一个翻译器您的变体(详细信息请参阅下面的链接!),但不要根据 JSON 特定内容“破解”内容!这些几乎是完全正交的方面!
  • 为 JSON 编写自己的读取器和编写器,应该可以轻松地从 Boost 中进行改编

优点:

  • 无需修改就可以根据 JSON 特定的细节来影响属性树
  • 不会污染 Boost 库及其名称空间,除了您自己的新库的专业化之外类型(变体翻译器)
  • 类型比基于自定义字符串的属性树方法更安全,
  • 对于许多不频繁序列化树的运行时场景来说应该更快

缺点:

  • 需要为相当小的行为细节付出一些努力,
  • 在 编译条件
  • 对于频繁序列化和树的微小变化(肯定可以优化)的运行时场景,
  • 可能会慢一些,将 json 读回到树中是一种有疑问的哲学工作,以确保之间尽可能多的对称性使用的类型(出于多种目的而不是学术问题)

有关更多详细信息,例如,请参阅

http:// marko-editor.com/articles/property_tree_store_anything/

您可以轻松地调整它以适应变体的使用。

All solutions, that require custom translators for strings explicitly, seem to be quite error prone for me since it's likely to forget it sometimes. It would be nice to have some kind of overload way via inheritance for the property tree's put method to handle this implicitly but that's not possible in a robust way since it's a template and you would have to ensure full covariance for all methods of the tree. Also changing boost library stuff as a workaround should be avoided in general if possible.

The most robust way without hacks I found so far is (since C++11):

  • use a boost-property tree with <KeyType, std::variant<yourTypes>>
  • Provide a translator for your variant (details see link below!) but do not "hackify" the content in terms of JSON-specifics! These are almost totally orthogonal aspects!
  • Write an own reader and writer for JSON, should be easily adapted from the Boost ones

Pros:

  • no hacks required to affect the property tree in terms of JSON-specific details
  • no pollution of the Boost libraries and their namespaces except the specializations of your new own types (the variant translator)
  • type safer than the custom string based property tree approach
  • should be faster for many runtime scenarios with non-frequent serialization of the tree

Cons:

  • requires some efforts for a quite small detail of the behavior
  • might be a bit slower in terms of compilation
  • might be a bit slower for runtime scenarios with frequent serialization and minor changes of the tree (can be optimized for sure)
  • reading the json back into the tree is some kind of philosophic work in doubt to ensure as much symmetry as possible between the used types (for many purposes rather an academic issue)

For more details, for instance see

http://marko-editor.com/articles/property_tree_store_anything/

You can easily adapt this for variant usage.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文