Подтвердить что ты не робот

Создание массивов JSON в Boost с использованием деревьев свойств

Я пытаюсь создать массив JSON, используя деревья свойств boost.

Документация говорит: "Массивы JSON сопоставляются с узлами. Каждый элемент является дочерним node с пустым именем."

Итак, я хотел бы создать дерево свойств с пустыми именами, а затем вызвать write_json(...), чтобы получить массив. Однако в документации не указано, как создавать неназванные дочерние узлы. Я попробовал ptree.add_child("", value), но это дает:

Assertion `!p.empty() && "Empty path not allowed for put_child."' failed

В документации, похоже, не говорится об этом, по крайней мере, я никак не могу понять. Может ли кто-нибудь помочь?

4b9b3361

Ответ 1

Простой массив:

#include <boost/property_tree/ptree.hpp>
using boost::property_tree::ptree;

ptree pt;
ptree children;
ptree child1, child2, child3;

child1.put("", 1);
child2.put("", 2);
child3.put("", 3);

children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));

pt.add_child("MyArray", children);

write_json("test1.json", pt);

приводит к:

{
    "MyArray":
    [
        "1",
        "2",
        "3"
    ]
}

Массив над объектами:

ptree pt;
ptree children;
ptree child1, child2, child3;


child1.put("childkeyA", 1);
child1.put("childkeyB", 2);

child2.put("childkeyA", 3);
child2.put("childkeyB", 4);

child3.put("childkeyA", 5);
child3.put("childkeyB", 6);

children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));

pt.put("testkey", "testvalue");
pt.add_child("MyArray", children);

write_json("test2.json", pt);

приводит к:

{
    "testkey": "testvalue",
    "MyArray":
    [
        {
            "childkeyA": "1",
            "childkeyB": "2"
        },
        {
            "childkeyA": "3",
            "childkeyB": "4"
        },
        {
            "childkeyA": "5",
            "childkeyB": "6"
        }
    ]
}

надеюсь, что это поможет

Ответ 2

Что вам нужно сделать, это удовольствие. Это из памяти, но для меня это работает.

boost::property_tree::ptree root;
boost::property_tree::ptree child1;
boost::property_tree::ptree child2;

// .. fill in children here with what you want
// ...

ptree.push_back( std::make_pair("", child1 ) );
ptree.push_back( std::make_pair("", child2 ) );

Но обратите внимание на несколько ошибок в разборе и написании json. Несколько из которых я отправил отчеты об ошибках - без ответа: (

РЕДАКТИРОВАТЬ: для беспокойства о его сериализации неправильно как { ":" ", ":" "}

Это происходит только тогда, когда массив является корневым элементом. Писатель boost ptree рассматривает все корневые элементы как объекты - никогда не массивы или значения. Это вызвано следующей строкой в ​​boost/propert_tree/detail/json_parser_writer.hpp

else if (indent > 0 && pt.count(Str()) == pt.size())

Избавление от "indent > 0 & &" позволит ему правильно писать массивы.

Если вам не нравится, сколько места создано, вы можете использовать патч, который я предоставил здесь

Ответ 3

Когда вы начинаете использовать Дерево свойств для представления структуры JSON, я сталкивался с аналогичными проблемами, которые я не разрешил. Также обратите внимание, что из документации дерево свойств не полностью поддерживает информацию типа:

Значения JSON отображаются на узлы, содержащие это значение. Однако вся информация типа теряется; числа, а также литералы "null", "true" и "false" просто сопоставляются с их строковой формой.

Узнав об этом, я переключился на более полную реализацию JSON JSON Spirit. Эта библиотека использует Boost Spirit для реализации грамматики JSON и полностью поддерживает JSON, включая массивы.

Я предлагаю вам использовать альтернативную реализацию JSON С++.

Ответ 4

В моем случае я хотел добавить массив к более или менее произвольному местоположению, поэтому, как и Майкл, создайте дочернее дерево и заполните его элементами массива:

using boost::property_tree::ptree;

ptree targetTree;
ptree arrayChild;
ptree arrayElement;

//add array elements as desired, loop, whatever, for example
for(int i = 0; i < 3; i++)
{
  arrayElement.put_value(i);
  arrayChild.push_back(std::make_pair("",arrayElement))
}

Когда ребенок был заполнен, используйте функцию put_child() или add_child(), чтобы добавить все дочернее дерево в целевое дерево, например...

targetTree.put_child(ptree::path_type("target.path.to.array"),arrayChild)

функция put_child берет путь и дерево для аргумента и будет "трансформировать" arrayChild в targetTree

Ответ 5

Начиная с boost 1.60.0 проблема сохраняется.

Предлагается обходной путь Python 3 (Gist), который можно вызвать сразу после boost::property_tree::write_json.

#!/usr/bin/env python3


def lex_leaf(lf: str):
    if lf.isdecimal():
        return int(lf)
    elif lf in ['True', 'true']:
        return True
    elif lf in ['False', 'false']:
        return False
    else:
        try:
            return float(lf)
        except ValueError:
            return lf

def lex_tree(j):
    tj = type(j)
    if tj == dict:
        for k, v in j.items():
            j[k] = lex_tree(v)
    elif tj == list:
        j = [lex_tree(l) for l in j]
    elif tj == str:
        j = lex_leaf(j)
    else:
        j = lex_leaf(j)
    return j


def lex_file(fn: str):
    import json
    with open(fn, "r") as fp:
        ji = json.load(fp)
    jo = lex_tree(ji)
    with open(fn, 'w') as fp:
        json.dump(jo, fp)


if __name__ == '__main__':
    import sys
    lex_file(sys.argv[1])

Ответ 6

Если вы хотите JSON в C++, нет необходимости в Boost. С помощью этой библиотеки вы можете получить JSON как тип данных первого класса, который ведет себя как контейнер STL.

// Create JSON on the fly.
json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};

// Or treat is as an STL container; create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);

// also use emplace_back
j.emplace_back(1.78);

// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
  std::cout << *it << '\n';
}