我编写了以下代码来表示C ++中的JSON数据。我得到一些模糊的评论,这可能不是最佳选择,如果我们决定将JSON数据直接解析到此结构中,则可能会遇到麻烦。我没有得到简短的注释,因此将在此处重现我的代码,并希望听到有什么问题。
#define BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT
#include <boost/variant.hpp>
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iomanip>
#define STR_(x) std::string(x)
struct JSONNullType {};
typedef boost::make_recursive_variant<
std::string,
long,
double,
std::map<std::string, boost::recursive_variant_>,
std::vector<boost::recursive_variant_>,
bool,
JSONNullType>::type JSONValue;
typedef std::vector<JSONValue> JSONArray;
typedef std::map<std::string, JSONValue> JSONObject;
struct JSONPrintVisitor : public boost::static_visitor<void>
{
void operator()(const JSONArray& array) const
{
std::cout << '[';
if (!array.empty()) {
boost::apply_visitor(*this, array[0]);
std::for_each(array.begin() + 1, array.end(),
[this](const JSONValue& v) {
std::cout << ',';
boost::apply_visitor(*this, v);
});
}
std::cout << ']' << std::endl;
}
void operator()(const JSONObject& object) const
{
std::cout << '{';
if (!object.empty()) {
const auto& kv_pair = *(object.begin());
std::cout << '"' << kv_pair.first << '"';
std::cout << ':';
boost::apply_visitor(*this, kv_pair.second);
auto it = object.begin();
std::for_each(++it, object.end(),
[this](const JSONObject::value_type& v) {
std::cout << ',';
std::cout << '"' << v.first << '"';
std::cout << ':';
boost::apply_visitor(*this, v.second);
});
}
std::cout << '}';
}
void operator() (const std::string& str) const
{
std::cout << '"' << str << '"';
}
void operator() (const JSONNullType&) const
{
std::cout << "null";
}
template <typename T>
void operator()(const T& value) const
{
std::cout << std::boolalpha << value;
}
};
int main()
{
JSONValue vt = JSONArray();
JSONArray *array = boost::get<JSONArray>(&vt);
JSONValue person1 = JSONObject();
JSONValue person2 = JSONObject();
JSONValue person3 = JSONObject();
JSONValue person4 = JSONObject();
JSONObject *pp1 = boost::get<JSONObject>(&person1),
*pp2 = boost::get<JSONObject>(&person2),
*pp3 = boost::get<JSONObject>(&person3);
(*pp1)["name"] = STR_("Baba O'Riley");
(*pp1)["profession"] = STR_("farmer");
(*pp1)["age"] = 21L;
(*pp1)["favourite"] = STR_("Baba ganoush");
(*pp1)["height"] = 176.1;
(*pp2)["name"] = STR_("Stuart Little");
(*pp2)["profession"] = STR_("good-boy mouse");
(*pp2)["age"] = 4L;
(*pp2)["favourite"] = STR_("Gouda");
(*pp2)["height"] = 11.0;
(*pp3)["name"] = STR_("Howard Roark");
(*pp3)["profession"] = STR_("architect");
(*pp3)["age"] = 32L;
(*pp3)["favourite"] = STR_("Eggs benedict");
(*pp3)["height"] = 185.0;
array->push_back(person1);
array->push_back(person2);
array->push_back(person3);
boost::apply_visitor(JSONPrintVisitor(), vt);
}
最佳答案
我的Boost Spirit解析器/生成器使用了非常相似的结构。
运作良好。
我会说
注意JSON,其将含义附加到对象中元素的顺序上;您的代表不保留该信息
注意对象中可能两次包含相同密钥的JSON(不会发生SHOULD
,但某些实现可能会发生)
JSON没有“ long”或“ double”。它只有“数字”。确保您的long具有64位(uint64_t
),并准备任意混合long / double(除非由于某种模式信息(例如Edm),您不知道该期待什么)
寻找导致模棱两可的构造函数的隐式转换。可能值得拥有一个强大的用户定义类型,而不是bool
,long
和double
。考虑如果发生的话会发生什么/应该发生什么
JSON::Value v(42u);
v = 7.5f;
对于某些应用程序,一个明显的优化是引用缓存的密钥,而不是为其复制字符串。 Boost Flyweight可能很方便(尽管在我的测试中,除非禁用对象跟踪,否则在解析过程中不会获得性能;您必须非常确定输入的合理性,以允许“不断增长的”缓存,因为这可能导致DoS)。