I'm newbie to C++. What's the easiest way to serialize and deserialize data of type std::Map
using boost
. I've found some examples with using PropertyTree
but they are obscure for me.
Serializing and deserializing JSON with Boost
Asked Answered
Note that property_tree
interprets the keys as paths, e.g. putting the pair "a.b"="z" will create an {"a":{"b":"z"}} JSON, not an {"a.b":"z"}. Otherwise, using property_tree
is trivial. Here is a little example.
#include <sstream>
#include <map>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
void example() {
// Write json.
ptree pt;
pt.put ("foo", "bar");
std::ostringstream buf;
write_json (buf, pt, false);
std::string json = buf.str(); // {"foo":"bar"}
// Read json.
ptree pt2;
std::istringstream is (json);
read_json (is, pt2);
std::string foo = pt2.get<std::string> ("foo");
}
std::string map2json (const std::map<std::string, std::string>& map) {
ptree pt;
for (auto& entry: map)
pt.put (entry.first, entry.second);
std::ostringstream buf;
write_json (buf, pt, false);
return buf.str();
}
I tried this (using Boost 1.57.0 for more or less the first time), and VS 2013 gives me C4512 warnings (assignment operator could not be generated). How does one solve that, apart from suppressing the warnings? –
Necroscopy
@Dabbler: I get no warnings with GCC 4.9.1 (
g++ -c -Wall -O2 -std=c++11 pt.cpp
). Don't have a VS. Also from googling I'd say the warning comes from somewhere deeper, becase there's no classes defined in the given code. So to answer your question, I think you should isolate the warning, checking if it is produced by the Boost code in property_tree, and if it does, then look for the corresponding Boost issue, filing a new issue if nobody did it yet. cf. boost.org/development/bugs.html –
Grogram Great answer. How about arrays? –
Aureomycin
Boost versions 1.75 and later now have a robust native JSON library:
https://www.boost.org/doc/libs/develop/libs/json/doc/html/index.html
I don't suggest using Boost.PropertyTree's JSON algorithms anymore, as they are not fully compliant with the spec.
Some company asked me to implement JSON serialization library which is faster than boost lib. I did that - it is ~10x times faster then boost lib. I publish the code for anyone to use.
#pragma once
#include <string>
#include <vector>
#include <regex>
#include <fstream>
enum class JsonNodeType { Array, Object, String };
class JsonNode
{
JsonNodeType m_nodeType;
std::vector<JsonNode*>* m_values{0};
std::vector<std::string>* m_keys{0};
std::string m_value{};
inline static int m_indent;
inline static bool m_formatOutput;
const int m_indentInc{4};
public:
JsonNode(JsonNodeType type)
{
m_nodeType = type;
switch (m_nodeType) {
case JsonNodeType::Object: m_keys = new std::vector<std::string>();
[[fallthrough]];
case JsonNodeType::Array: m_values = new std::vector<JsonNode*>();
}
};
JsonNode(std::string value)
{
m_nodeType = JsonNodeType::String;
m_value = value;
}
~JsonNode()
{
if (m_values)
for (JsonNode* node : *m_values)
delete node;
delete m_values;
delete m_keys;
}
void Add(JsonNode* node)
{
assert(m_nodeType == JsonNodeType::Array);
m_values->push_back(node);
}
void Add(const char* key, JsonNode* node)
{
assert(m_nodeType == JsonNodeType::Object);
m_values->push_back(node);
m_keys->push_back(key);
}
void Add(const char* key, std::string value)
{
assert(m_nodeType == JsonNodeType::Object);
m_keys->push_back(key);
m_values->push_back(new JsonNode(value));
}
void Add(std::string value)
{
assert(m_nodeType == JsonNodeType::Array);
m_values->push_back(new JsonNode(value));
}
void Add(int value)
{
assert(m_nodeType == JsonNodeType::Array);
m_values->push_back(new JsonNode(std::to_string(value)));
}
void Add(const char* key, bool value)
{
assert(m_nodeType == JsonNodeType::Object);
m_keys->push_back(key);
m_values->push_back(new JsonNode(value ? "true" : "false"));
}
void OutputToStream(std::ostream& ofs, bool formatOutput = true)
{
m_indent = 0;
m_formatOutput = formatOutput;
OutputNodeToStream(ofs);
ofs << std::endl;
}
std::string EscapeString(std::string& str)
{
std::regex html2json("\\\\|\\/|\\\"");
std::regex newline("\n");
std::string tmp = std::regex_replace(str, html2json, "\\$&");
return std::regex_replace(tmp, newline, "\\n");
}
private:
void OutputNodeToStream(std::ostream& ofs)
{
switch (m_nodeType) {
case JsonNodeType::String:
ofs << "\"" << m_value << "\"";
break;
case JsonNodeType::Object:
OutputObjectToStream(ofs);
break;
case JsonNodeType::Array:
OutputArrayToStream(ofs);
break;
}
}
void ChangeIndent(std::ostream& ofs, int indentDelta)
{
if (!m_formatOutput)
return;
m_indent += indentDelta;
ofs << std::endl;
}
void OutputIndents(std::ostream& ofs)
{
if (!m_formatOutput)
return;
for (int i = 0; i < m_indent; i++)
ofs << " ";
}
void OutputObjectToStream(std::ostream& ofs)
{
assert(m_nodeType == JsonNodeType::Object);
assert(m_keys->size() == m_values->size());
if (m_keys->empty())
{
ofs << "\"\"";
return;
}
ofs << "{";
ChangeIndent(ofs, m_indentInc);
for (int i = 0; i < m_keys->size(); i++)
{
if (i > 0)
ofs << ",";
if (i > 0 && m_formatOutput)
ofs << std::endl;
OutputIndents(ofs);
ofs << "\"" << m_keys->at(i) << "\": ";
m_values->at(i)->OutputNodeToStream(ofs);
}
ChangeIndent(ofs, -m_indentInc);
OutputIndents(ofs);
ofs << "}";
}
void OutputArrayToStream(std::ostream& ofs)
{
assert(m_nodeType == JsonNodeType::Array);
if (m_values->empty())
{
ofs << "\"\"";
return;
}
ofs << "[";
ChangeIndent(ofs, m_indentInc);
for (int i = 0; i < m_values->size(); i++)
{
if (i > 0)
ofs << ",";
if(i > 0 && m_formatOutput)
ofs << std::endl;
OutputIndents(ofs);
m_values->at(i)->OutputNodeToStream(ofs);
}
ChangeIndent(ofs, -m_indentInc);
OutputIndents(ofs);
ofs << "]";
}
};
Usage examples
Create json tree:
JsonNode* Circuit::GetMyJson()
{
JsonNode* node = new JsonNode(JsonNodeType::Object);
JsonNode* gates = new JsonNode(JsonNodeType::Array);
for (auto& [k, v] : m_gates)
gates->Add(v.GetMyJson());
node->Add("gates", gates);
return node;
}
Output tree:
std::unique_ptr<JsonNode> node (simulation->GetMyJson());
std::ofstream output("output.json", std::ios::out);
node->OutputToStream(output);
Very interesting! Do you have some benchmark results that show how much faster this runs? –
Burgenland
© 2022 - 2024 — McMap. All rights reserved.