使用Boost Variants在C ++中表示JSON数据

时间:2014-05-27 05:46:24

标签: c++ json boost boost-variant

我编写了以下代码来表示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);
}

1 个答案:

答案 0 :(得分:2)

我使用与Boost Spirit解析器/生成器非常相似的结构。

它运作良好。

我说

  • 注意JSON,它附带对象中元素的顺序;你的代表不保留这些信息
  • 注意可能在对象中包含相同键两次的JSON(它不会发生SHOULD,但某些实现可能会发生)
  • JSON没有&#34; long&#34;或&#34; double&#34;。它只有&#34;数字&#34;。确保你的long有64位(uint64_t)并准备任意混合long / double(除非你知道由于某些架构信息如Edm所期望的)
  • 寻找导致模糊构造函数的隐式转换。拥有强大的用户定义类型而不是boollongdouble可能是值得的。考虑如果你说

    会发生什么/应该发生什么
    JSON::Value v(42u);
    v = 7.5f;
    

某些应用程序的一个明显优化是引用缓存的密钥而不是为它们复制字符串。 Boost Flyweight可能很方便(虽然在我的测试中,除非禁用对象跟踪,否则在解析期间不会获得性能;您必须非常确定您的输入理智以允许“不断增长的”缓存,因为它可能导致DoS)。