c++ - 编写 JSON 解释器的设计问题

标签 c++ design-patterns c++11

我决定用 c++ 编写一个 json 解释器来练习。理想情况下,我希望能够将其设置在一个包含 map 、 vector 和相关值类型的树状容器中,以便我可以按照 json 的实际结构方式进行访问。例如。给定以下 JSON 示例(来自 json.org):

JSON
{
"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}


C++
jsonobject["menu"]["id"] // returns std::string "file"
jsonobject["menu"]["popup"]["menuitem"] // returns std::vector of std::maps
jsonobject["menu"]["popup"]["menuitem"][0]["value"] // returns std::string "New"

在上面的例子中,我的第一个问题是容器内有混合类型。例如,在上面的 json 中,“menu”将是一个 std::map,但我不能有键“id”和“popup”,因为一个返回字符串,另一个返回 vector 。

为了解决这个问题,我决定创建从无类型基类继承的包装器模板类。假设这将提供对值的多态访问。问题是我做不到。这里有一些代码来展示我到目前为止所拥有的:

#include <string>
#include <map>
#include <vector>

class NodeBase {};

template <typename T>
class Node : public BaseNode {};

typedef std::map<std::string, BaseNode*> JSONObject;
template <>
class Node<JSONObject> : public BaseNode {
  public:
    JSONObject value;
    BaseNode* operator[](const std::string key){(value.find(key) != value.end) ? return value[key] : return nullptr}
};

typedef std::vector<BaseNode*> JSONArray;
template <>
class Node<JSONArray> : public BaseNode {
  public:
    JSONArray value;
    BaseNode* operator[](const uint index) {(index < value.size()) ? return value[index] : return nullptr}
};

template <typename T>
class Node : public BaseNode {
  public:
    T value;
};

class RootNode {
  Node<JSONObject> value;
};

int main(void) {
  RootNode root;
  root.insert(std::pair<std::string, BaseNode*>("menu", new Node<JSONObject>)
  // problem!
  // cannot use following code, because BaseNode* does not have access to value :'<
  root["menu"].insert(..)
}

所以我想我的问题是,我该如何进行这项工作?我是在正确的道路上,但由于缺乏经验而看不到解决方案,还是这种设计只是与 C++ 不兼容?

最佳答案

有趣的类(class),但我最近使用 boost::property_three 来解析 json 并且很高兴我避免了重新发明轮子 xD 你的库可能作为轻量级解析器工作,所以我对这项研究感兴趣。

我觉得这个设计不太好,因为用library的人可能不知道某个value包含的是什么:hash还是array。这可能导致 dynamic_cast 过载这是比较繁重的操作。结果,您的图书馆失去了它的“轻量级”属性。

我建议使用一种对象类型表示法,就像所有 Get 一样方法返回 Node* ,但是Node*可以回答问题isArray() , isHash()无需像动态转换这样繁重的操作。

接下来,Node<JSONObject>Node<JSONArray>应该有相同的接口(interface)继承自 BaseNode : BaseNode* operator[](const std::string key) ,但是Node<JSONArray>能够收到intoperator[] 键重载。这缩小了 API 并允许用户使用 int索引,如果他知道它是数组。但这不是万能的,你应该去做所谓的“图书馆设计”。

在全局范围内,我建议现在不要编写代码。写一些类层次架构,写一些用例;看看这些用例如何适应模式。一般来说,编写这样的库并不是一件容易的事,“设计”错误可能会使您的库将来无法使用。所以,明智地选择 =) 祝你好运!

关于c++ - 编写 JSON 解释器的设计问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12577231/

相关文章:

c++ - STL 算法和并发编程

c++ - 与 glog 库链接

c++ - 作为 C++11 属性参数的变量

c++ - 无法连接到端口,但 netstat 显示正在监听的端口

c++ - asio缓冲区深拷贝

java - 打破外部 API 调用的模式?

c++ - 设计问题 - 观察者、工厂、组合等等

c# - 实现两个不同类的相似方法的最佳方法?

c++ - 是否可以在 constexpr 中使用 std::string ?

c++ - 在循环中将值传递给数组