使用函数模板来写一个简单高效的 JSON 查询器,需要以下步骤:
1. 定义 JSON 数据结构
首先需要定义一个JSON数据结构,以便对其进行查询。这里我们将使用一个基于std::map的存储结构来表示JSON对象。其中,每个JSON对象的键值对都将被存储为std::map中的一对键-值。对于嵌套的JSON对象,我们可以将其表示为std::map的嵌套结构。
class Json
{
public:
using Dict = std::map<std::string, Json>;
using List = std::vector<Json>;
enum Type
{
Null,
Boolean,
Number,
String,
Array,
Object
};
Json() : type_(Null) {}
Json(std::nullptr_t) : type_(Null) {}
Json(bool value) : type_(Boolean), bool_v_(value) {}
Json(int value) : type_(Number), int_v_(value) {}
Json(double value) : type_(Number), double_v_(value) {}
Json(const std::string& value) : type_(String), string_v_(value) {}
Json(const Dict& value) : type_(Object), dict_v_(value) {}
Json(const List& value) : type_(Array), list_v_(value) {}
template <typename T>
T get() const;
Type type() const { return type_; }
bool is_null() const { return type_ == Null; }
bool is_boolean() const { return type_ == Boolean; }
bool is_number() const { return type_ == Number; }
bool is_string() const { return type_ == String; }
bool is_array() const { return type_ == Array; }
bool is_object() const { return type_ == Object; }
std::string to_string() const;
// do query using a JsonPath expression
Json query(const std::string& path) const;
private:
Type type_;
union
{
bool bool_v_;
int int_v_;
double double_v_;
std::string string_v_;
Dict dict_v_;
List list_v_;
};
};
2. 定义 JsonPath
然后,需要定义一种查询语言——JsonPath,用于查询JSON中的元素。JsonPath是一种基于字符串的语言,可以按照指定的路径从JSON对象中检索出相应的元素,类似于XPath在XML文档中的使用。
这里,我们将采用由Jayway公司制定的JsonPath标准,其中包含了十分丰富的查询选项,可以准确定位和检索JSON中的数据。例如,使用JsonPath可以对列表中的子元素,取出特定的键值对,或者在多嵌套层次的JSON数据中取出指定深度的子元素等。
以下是JsonPath的常见符号和使用方法:
符号 | 意义 |
---|---|
$ | 根元素 |
. | 点,后接属性名称 |
[] | 下标或属性名称 |
* | 所有 |
[start:end] | 切片选取 |
[?(expr)] | 过滤器 |
例如,我们要从JSON数据中取出第二个元素,可以写为:$[1]
。
3. 编写 Json 查询器函数模板
接下来,我们将定义查询函数模板。查询函数模板可以接受任何类型的JSON结构作为输入参数,并按照给定的JsonPath表达式进行查询。查询结果将以Json对象的形式返回给用户。
代码实现:
class JsonParseError
{
public:
JsonParseError(const std::string& msg) : message_(msg) {}
const std::string& message() const { return message_; }
private:
std::string message_;
};
template <typename T>
T Json::get() const
{
if constexpr (std::is_same_v<T, bool>)
{
return bool_v_;
}
else if constexpr (std::is_same_v<T, int>)
{
return int_v_;
}
else if constexpr (std::is_same_v<T, double>)
{
return double_v_;
}
else if constexpr (std::is_same_v<T, std::string>)
{
return string_v_;
}
else if constexpr (std::is_same_v<T, Dict>)
{
if (is_object())
return dict_v_;
else
throw JsonParseError("Json object expected");
}
else if constexpr (std::is_same_v<T, List>)
{
if (is_array())
return list_v_;
else
throw JsonParseError("Json array expected");
}
else if constexpr (std::is_same_v<T, Json>)
{
return *this;
}
else
{
throw std::invalid_argument("Unsupported type");
}
}
// 递归函数实现JsonPath的语法解析和查询
template <typename T>
void query_path(const T& value, std::deque<std::string>& path, Json& result)
{
if (path.empty())
{
result = value;
return;
}
std::string name = path.front();
path.pop_front();
if (value.is_object())
{
const auto& dict = value.get<Dict>();
auto it = dict.find(name);
if (it == dict.end())
{
result = Json();
return;
}
else
{
query_path(it->second, path, result);
return;
}
}
else if (value.is_array())
{
// 获取数组下标
if (name == "*")
{
// 如果表达式中包含*表示查询所有元素,注意这里返回的是一个数组
const auto& list = value.get<List>();
Json::List result_list;
for (const auto& item : list)
{
Json temp;
query_path(item, path, temp);
if (!temp.is_null())
result_list.push_back(temp);
}
result = Json(result_list);
return;
}
else
{
int index = std::stoi(name);
const auto& list = value.get<List>();
if (index < 0 || index >= static_cast<int>(list.size()))
{
result = Json();
return;
}
else
{
query_path(list[index], path, result);
return;
}
}
}
else
{
result = Json();
return;
}
}
Json Json::query(const std::string& path_expr) const
{
if (path_expr.empty())
return Json();
if (path_expr.front() != '$')
throw JsonParseError("JsonPath must start with $");
std::deque<std::string> path;
// 解析JsonPath
for (auto it = path_expr.begin() + 1; it != path_expr.end();)
{
if (*it == '.')
{
++it;
auto pos = path_expr.find_first_of(".$[", it - path_expr.begin());
if (pos == std::string::npos)
{
path.emplace_back(it, path_expr.end());
it = path_expr.end();
}
else
{
path.emplace_back(it, it + pos - (it - path_expr.begin()));
it += pos - (it - path_expr.begin());
}
}
else if (*it == '[')
{
++it;
if (*it == '\'' || *it == '\"')
{
auto pos = path_expr.find_first_of("\'\"", it + 1 - path_expr.begin());
if (pos == std::string::npos || *(it + pos + 1) != ']')
throw JsonParseError("Invalid JsonPath expression");
path.emplace_back(it + 1, it + pos);
it = it + pos + 2;
}
else if (std::isdigit(*it))
{
auto pos = path_expr.find_first_of("]", it - path_expr.begin());
if (pos == std::string::npos)
throw JsonParseError("Invalid JsonPath expression");
path.emplace_back(it, it + pos - (it - path_expr.begin()));
it += pos - (it - path_expr.begin()) + 1;
}
else if (*it == '*')
{
path.emplace_back("*");
it += 2;
}
else
{
throw JsonParseError("Invalid JsonPath expression");
}
}
else
{
throw JsonParseError("Invalid JsonPath expression");
}
}
Json result;
query_path(*this, path, result);
return result;
}
4. 测试 Json 查询器
使用函数模板编写的 Json 查询器实现后,我们需要进行一些测试来确保其正常工作。以下是两个示例:
// 测试JsonPath:$.name
Json test_json = Json("{\"name\": \"Tom\", \"age\": 20}");
Json result = test_json.query("$.name");
std::cout << "Result: " << result.to_string() << std::endl;
// 预期输出:Result: "Tom"
// 测试JsonPath:$..name
Json test_json = Json("{\"name\": \"Tom\", \"age\": 20, \"friends\": [{\"name\": \"Mike\", \"age\": 21}]}");
Json result = test_json.query("$..name");
std::cout << "Result: " << result.to_string() << std::endl;
// 预期输出:Result: ["Tom", "Mike"]
以上是使用函数模板,写一个简单高效的 JSON 查询器的方法介绍,包含定义JSON数据结构,定义JsonPath,编写Json查询器函数模板,以及测试Json查询器的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:用函数模板,写一个简单高效的 JSON 查询器的方法介绍 - Python技术站