Создание массивов JSON в Boost с использованием деревьев свойств - PullRequest
65 голосов
/ 22 января 2010

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

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

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

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

Документация, кажется, не решает эту проблему, по крайней мере, я никак не могу понять. Кто-нибудь может помочь?

Ответы [ 5 ]

100 голосов
/ 10 октября 2012

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

#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"
        }
    ]
}

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

20 голосов
/ 22 января 2010

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

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. Некоторые из которых я отправил сообщения об ошибках - без ответа: (

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

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

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

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

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

11 голосов
/ 22 января 2010

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

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

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

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

6 голосов
/ 12 апреля 2012

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

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

0 голосов
/ 29 октября 2018

По состоянию на 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])
...