web-dev-qa-db-fra.com

Pourquoi l'arborescence des propriétés Boost write_json enregistre-t-elle tout sous forme de chaîne? Est-il possible de changer cela?

J'essaie de sérialiser en utilisant l'arborescence de propriétés de boost write_json, il enregistre tout sous forme de chaînes, ce n'est pas que les données sont erronées, mais j'ai besoin de les convertir explicitement à chaque fois et je veux les utiliser ailleurs. (comme dans python ou autre bibliothèque C++ json (non boost))

voici un exemple de code et ce que j'obtiens en fonction des paramètres régionaux:

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();

et my_string_to_send_somewhere_else est qc. comme ça:

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

Existe-t-il de toute façon de les enregistrer en tant que valeurs, comme: "key1" : true ou "key2" : 2.2?

60
pprzemek

La solution la plus simple et la plus propre que j'ai pu trouver était de générer le JSON avec des espaces réservés et à la fin de la chaîne de remplacer par la valeur réelle en abandonnant les guillemets supplémentaires.

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);
}

Et le résultat est

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

6
sbile

Ok, je l'ai résolu comme ça, (bien sûr, il ne conviendra pas à tout le monde, car c'est un peu un hack, qui a besoin de plus de travail).


J'ai écrit ma propre fonction write_json (Simplement copié les fichiers, json_parser.hpp Et json_parser_write.hpp Dans mon projet) et modifié les lignes suivantes dans json_parser_write.hpp:

  1. ligne 37 commentée - échapper à la citation ""
  2. ligne 76 modifiée - pour qu'il n'ajoute plus de guillemets: stream << Ch('"') << data << Ch('"'); ==> stream << data;

Ensuite, les valeurs seront enregistrées correctement, sauf pour les chaînes, j'ai donc écrit un traducteur personnalisé:

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 +'"'; }
};

et simplement enregistré la chaîne en utilisant:

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

programme complet:

#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;
}

résultat :)

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

Boost confirme que sa mise en œuvre n'est pas conforme à 100% à la norme JSON. Consultez le lien suivant pour voir leur explication: Faire une variante de ptree qui préserve les types JSON est un plan futur, mais loin de là. !

10
Alex Sed

Comme nous l'avons typéef basic_ptree <std :: string, std :: string> ptree; dans les bibliothèques boost, boost sérialisera toujours chaque valeur sous forme de chaîne et analysera toutes les valeurs en une chaîne équivalente.

3
Josbert Lonnee

D'après le JSON généré, il est clair que le sérialiseur sérialise tout en chaînes en utilisant une sorte de méthode .toString () - c'est-à-dire qu'il ne connaît pas le type de chaque membre et enferme donc tout dans "".

Voir Création de tableaux JSON dans Boost en utilisant des arborescences de propriétés pour plus d'informations sur ce problème.

2
Sean Kinsey

J'ai fini par ajouter une autre fonction à mes utilitaires pour résoudre ce problème:

#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();
    }
} }

J'espère que cela pourra aider.

0
user7867434