You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中实现支持各类类数组类型及嵌套结构的JSON构造函数?

JSON解析器多形式数组初始化实现方案

问题背景

正在参考nlohmann的实现练习编写JSON解析器,希望json类支持多种数组形式的初始化:

  • 初始化列表:如json j0{1,2,"Hello",3.14,true,false,std::nullptr}
  • 多维原生数组:如int nums1[][3]={{1,2,3},{4,5,6}}
  • std::vector/std::array/std::list等序列容器
  • 嵌套的数组/容器/初始化列表

已给出json类的部分定义:

class json {
 private:
  /* Alias of json_value data types */
  using json_array = std::vector<json_value>;
  // 假设已定义json_object等其他类型别名
  using json_object = std::map<std::string, json_value>;

  union json_value {
    json_array* arr;
    json_object* obj;
    bool boolean;
    double number;
    std::string* str;
    std::nullptr_t null;
  };

  json_value value_;
  // 需补充类型标记(如枚举)记录当前value_的实际类型,此处省略
 public:
  // 待实现构造函数
};

想请教:

  1. 如何实现支持序列容器的构造函数?
  2. 如何支持任意维度的嵌套数组/容器/初始化列表?

考虑过用is_detected检查迭代器是否存在来判断序列容器,但想知道是否有更优方案;目前对任意嵌套数组的实现毫无头绪。


解决方案

1. 支持序列容器的构造函数实现

方案一:C++20 Concept(推荐)

用Concept直接约束序列容器类型,代码简洁直观,同时排除字符串类型(避免将std::string误判为数组):

#include <concepts>
#include <iterator>

template <typename Container>
concept SequenceContainer = requires(Container c) {
    std::begin(c);
    std::end(c);
    !std::is_same_v<std::decay_t<Container>, std::string>;
};

class json {
    // ... 原有成员 ...
public:
    // 序列容器构造函数
    template <SequenceContainer Container>
    json(const Container& container) {
        value_.arr = new json_array();
        for (const auto& elem : container) {
            value_.arr->emplace_back(json(elem).value_);
        }
        // 设置类型标记,如 type_ = json_type::array;
    }
};

方案二:SFINAE(兼容C++11/17)

如果使用C++20之前的标准,可结合std::enable_if和自定义检测traits实现:

#include <type_traits>

// 检测类型是否有begin()成员
template <typename T>
using has_begin_t = decltype(std::declval<T>().begin());

template <typename T>
constexpr bool is_sequence_container_v = 
    std::is_detected_v<has_begin_t, T> &&
    !std::is_same_v<std::decay_t<T>, std::string>;

class json {
    // ... 原有成员 ...
public:
    template <typename Container, typename = std::enable_if_t<is_sequence_container_v<Container>>>
    json(const Container& container) {
        value_.arr = new json_array();
        for (const auto& elem : container) {
            value_.arr->emplace_back(json(elem).value_);
        }
        // 设置类型标记
    }
};

2. 任意维度嵌套的实现

核心思路是递归构造:只要输入类型可转换为json(包括嵌套的容器、数组、初始化列表),就递归处理每个元素。

2.1 支持初始化列表

先实现初始化列表构造函数,天然支持嵌套结构:

class json {
    // ... 原有成员 ...
public:
    // 初始化列表构造
    json(std::initializer_list<json> init) {
        value_.arr = new json_array();
        for (const auto& elem : init) {
            value_.arr->emplace_back(elem.value_);
        }
        // 设置类型标记
    }

    // 基本类型构造函数(必须优先实现,避免初始化列表匹配错误)
    json(bool b) { value_.boolean = b; /* 设置类型标记 */ }
    json(double num) { value_.number = num; /* 设置类型标记 */ }
    json(const char* s) { value_.str = new std::string(s); /* 设置类型标记 */ }
    json(const std::string& s) { value_.str = new std::string(s); /* 设置类型标记 */ }
    json(std::nullptr_t) { value_.null = nullptr; /* 设置类型标记 */ }
};

此时json j{1, "hello", {2, 3, true}}这类嵌套初始化列表可直接正常解析。

2.2 支持多维原生数组

用模板递归处理数组维度,利用std::rank判断数组是一维还是多维:

#include <type_traits>

class json {
    // ... 原有成员 ...
public:
    // 一维数组构造
    template <typename T, std::size_t N>
    json(const T (&arr)[N]) {
        value_.arr = new json_array();
        for (std::size_t i = 0; i < N; ++i) {
            value_.arr->emplace_back(json(arr[i]).value_);
        }
        // 设置类型标记
    }

    // 多维数组构造(递归)
    template <typename T, std::size_t N, std::size_t... Rest>
    json(const T (&arr)[N][Rest...]) {
        value_.arr = new json_array();
        for (std::size_t i = 0; i < N; ++i) {
            // 递归构造子数组对应的json对象
            value_.arr->emplace_back(json(arr[i]).value_);
        }
        // 设置类型标记
    }
};

这样int nums[2][3] = {{1,2,3}, {4,5,6}}; json j(nums);就能正确解析为二维JSON数组。

2.3 嵌套容器的自动支持

序列容器的构造函数会遍历每个元素,若元素本身是序列容器、数组或初始化列表,会自动调用对应的json构造函数,因此天然支持嵌套,例如:

std::vector<std::list<int>> nested = {{1,2}, {3,4,5}};
json j(nested); // 自动解析为二维JSON数组

注意事项

  1. 内存管理:json_value使用指针存储数组/对象/字符串,必须实现正确的析构函数、拷贝构造、移动构造等,避免内存泄漏。
  2. 类型标记:必须为json类添加类型标记(如枚举json_type { array, object, boolean, number, string, null }),用于记录value_当前存储的实际类型,否则无法正确访问和析构。
  3. 构造函数优先级:基本类型的构造函数要优先于序列容器/数组的构造函数,避免编译器匹配错误(比如const char*被误判为序列容器)。

内容的提问来源于stack exchange,提问作者creepy-otter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 07:50:26