Создание массивов JSON в Boost с использованием деревьев свойств
Я пытаюсь создать массив JSON, используя деревья свойств boost.
Документация говорит: "Массивы JSON сопоставляются с узлами. Каждый элемент является дочерним node с пустым именем."
Итак, я хотел бы создать дерево свойств с пустыми именами, а затем вызвать write_json(...)
, чтобы получить массив. Однако в документации не указано, как создавать неназванные дочерние узлы. Я попробовал ptree.add_child("", value)
, но это дает:
Assertion `!p.empty() && "Empty path not allowed for put_child."' failed
В документации, похоже, не говорится об этом, по крайней мере, я никак не могу понять. Может ли кто-нибудь помочь?
Ответы
Ответ 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';
}