69 votes

Pourquoi le fichier write_json de l'arbre de propriétés Boost enregistre-t-il tout sous forme de chaîne de caractères ? Est-il possible de changer cela ?

J'essaie de sérialiser en utilisant l'arbre de propriété write_json de boost, il enregistre tout comme des chaînes, ce n'est pas que les données sont mauvaises, mais je dois les couler explicitement à chaque fois et je veux les utiliser ailleurs. (comme dans python ou une autre bibliothèque C++ json (non boost))

Voici un exemple de code et ce que j'obtiens en fonction de la 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();

et my_string_to_send_somewhere_else c'est quelque chose comme ça :

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

Existe-t-il un moyen de les sauvegarder en tant que valeurs, par exemple : "key1" : true o "key2" : 2.2 ?

38voto

pprzemek Points 839

Ok, j'ai résolu le problème de la manière suivante (bien sûr, cela ne conviendra pas à tout le monde, car il s'agit d'une sorte de hack, qui nécessite un travail supplémentaire).


J'ai écrit mon propre write_json (a simplement copié les fichiers, json_parser.hpp y json_parser_write.hpp à mon projet) et modifié les lignes suivantes dans json_parser_write.hpp :

  1. commentaire ligne 37 - échappement des guillemets '"'.
  2. modifié la ligne 76 - pour qu'elle n'ajoute plus de guillemets : stream << Ch('"') << data << Ch('"'); ==> stream << data;

Les valeurs seront alors enregistrées correctement, sauf pour les chaînes de caractères, pour lesquelles j'ai é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 sauvegarder 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"
            }
        ]
    }
}

5 votes

+1 utile de savoir qu'il n'y a pas de solution propre pour l'instant, et qu'un bricolage est nécessaire.

1 votes

Si le guillemet double n'est pas échappé, vous risquez de rencontrer un problème si ce guillemet se trouve dans votre chaîne.

0 votes

À la fin, est-ce que ça marche pour l'écriture et la lecture aussi. Ou est-ce en écriture seulement ?

14voto

Alex Sed Points 11

Boost confirme que son implémentation n'est pas 100% conforme à la norme JSON. Consultez le lien suivant pour voir leur explication : La création d'une variante de ptree qui préserve les types JSON est un projet futur, mais il est encore loin. !

6voto

sbile Points 61

La solution la plus simple et la plus propre que j'ai pu trouver était de générer le JSON avec des placeholders et de remplacer la chaîne de caractères par la valeur réelle sans 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

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

6voto

J'ai fini par ajouter une autre fonction à mes utils 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 vous aidera.

3voto

Josbert Lonnee Points 11

Comme nous avons typedef basic_ptree<std::string, std::string> ptree ; dans les bibliothèques de boost, boost va toujours sérialiser chaque valeur en tant que chaîne et analyser toutes les valeurs en un équivalent chaîne.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X