我有一个复杂的JSON对象,带有一个设置列表,如下所示(示例是人为设计的):
{
"configuration" : {
"name": "<room name>",
"players": {"min": 3, "max": 5},
"options": {
"rounds": {"prompt": "Enter number of rounds", "default": 10},
"score_to_win": {"prompt": "Enter score to win", "default": 500}
}
}
// ... lots of other information, can get pretty deeply nested
}我可以使用如下代码处理它(使用nlohmann-json):
void parse_json(const nlohmann::json &j) {
nlohmann::json config = j.at("configuration");
std::string name = config.at("name");
int minplayers = config.at("players").at("min");
int maxplayers = config.at("players").at("max");
for (auto& it : config.at("options").items()) {
std::string key = it.key();
std::string prompt = it.value().at("prompt");
int defval = it.value().at("default");
}
}但是如果JSON不正确,它不会给出非常有用的错误消息:
try {
parse_json(nlohmann::json::parse(R"({"configuration" : {
"name": "cool room name",
"players": {"min": 4, "max": "6"},
"options": {
"rounds": {"prompt": "Enter number of rounds", "default": 10},
"score_to_win": {"prompt": "Enter score to win", "default": 500}
}
}})"));
} catch (std::exception &ex) {
std::cout << ex.what() << "\n";
}
// [json.exception.type_error.302] type must be number, but is string我可以将try/catches包含在每一行中,以说明异常发生在何处,但这会使代码变得混乱。有没有一种方法可以报告JSON中的错误位置,而不会把代码弄得太乱?
发布于 2020-10-13 06:44:31
您可以跟踪最后访问的路径(作为JSON指针),并报告其上的错误:
void parse_json(const nlohmann::json &j) {
nlohmann::json::json_pointer last_accessed;
auto at = [&j, &last_accessed](const nlohmann::json::json_pointer& ptr) {
last_accessed = ptr;
return j.at(ptr);
};
try {
auto config_ptr = "/configuration"_json_pointer;
nlohmann::json config = at(config_ptr);
std::string name = at(config_ptr / "name");
...
} catch (...) {
throw last_accessed;
}
}使用稍微智能一点的异常类型,您可以报告底层的nlohmann::json错误,但我希望您能理解它的要点。
发布于 2020-10-15 13:30:05
基于@Botje的想法,我创建了一个帮助器类来更新json指针。这就是它:
#include <string>
#include "nlohmann/json.hpp"
/*
LastAccessedHelper(json, json_pointer)
Construct with a JSON object and a json_pointer to store the access path in
Supports const or non-const access, change the type alias in the definition accordingly
"arg" below can be a json_pointer, string, or size_t
at(arg) - return json at a given position
at() - access json at root -- effectively "convert" helper to json object
enter(arg) - move last_accessed to a new location and return child helper based at new location
exit() - tell child helper to move last_accessed back to the previous location
*/
// helper function to count the number of levels in a pointer, no native way of doing it
int json_ptr_size(const nlohmann::json::json_pointer &_ptr) {
// make a copy and pop_back() until the root :(
nlohmann::json::json_pointer ptr(_ptr);
int size = 0;
while (!ptr.empty()) {
ptr.pop_back();
size++;
}
return size;
}
struct LastAccessedHelper {
using json = const nlohmann::json; // set to be const or non-const based on your use case
using json_pointer = nlohmann::json::json_pointer;
json &j;
json_pointer &last_accessed;
int at_level;
int exit_level;
LastAccessedHelper(json &j, json_pointer &last_accessed)
: j{j}, last_accessed{last_accessed}, at_level{0}, exit_level{0}
{}
LastAccessedHelper(json &j, json_pointer &last_accessed, int exit_level)
: j{j}, last_accessed{last_accessed}, at_level{0}, exit_level{exit_level}
{}
/* at() - return json at a given position */
json& at(const json_pointer& ptr) {
reset();
at_level = json_ptr_size(ptr);
last_accessed /= ptr;
return j.at(ptr);
}
json& at(const std::string &s) {
return at(json_pointer() / s);
}
json& at(size_t t) {
return at(json_pointer() / t);
}
/* at() no arguments - "convert" LastAccessedHelper to a json object */
json& at() {
return at(json_pointer());
}
/* enter() - move last_accessed to a new location and return child helper based at new location */
LastAccessedHelper enter(const json_pointer& ptr) {
reset();
last_accessed /= ptr;
return LastAccessedHelper(j.at(ptr), last_accessed, json_ptr_size(ptr));
}
LastAccessedHelper enter(const std::string &s) {
return enter(json_pointer() / s);
}
LastAccessedHelper enter(size_t t) {
return enter(json_pointer() / t);
}
/* exit() - tell child helper to move last_accessed back to the previous location */
void exit() {
reset();
unwind(exit_level);
exit_level = 0;
}
/* helpers */
void unwind(int levels) {
for (int i=0; i<levels; i++) {
last_accessed.pop_back();
}
}
void reset() {
unwind(at_level);
at_level = 0;
}
};对@Botje解决方案的主要更改是
enter()/exit(),该功能在新位置创建帮助器以减少间接级别。使用它,示例代码如下所示:
void parse_json(const nlohmann::json &j) {
nlohmann::json::json_pointer last_accessed;
LastAccessedHelper acc{j, last_accessed};
try {
auto config = acc.enter("configuration");
std::string name = config.at("name");
int maxplayers = config.at("/players/max"_json_pointer);
int minplayers = config.at("/players/min"_json_pointer);
auto options = config.enter("options");
for (auto& [key,val] : options.at().items()) {
nlohmann::json::json_pointer key_ptr;
key_ptr /= key;
std::string prompt = options.at(key_ptr / "prompt");
int defval = options.at(key_ptr / "default");
}
options.exit();
config.exit();
} catch (std::exception &ex) {
throw std::runtime_error("Exception at " + last_accessed.to_string() + ": " + ex.what());
}
}您可以在子函数中使用它,也可以递归使用它:
void parse_tree(const nlohmann::json &j, nlohmann::json::json_pointer &last_accessed) {
LastAccessedHelper acc{j, last_accessed};
int value = acc.at("value");
for (auto& [key,val] : acc.at("children").items()) {
last_accessed /= key;
parse_tree(val, last_accessed);
last_accessed.pop_back();
}
}
void parse_json(const nlohmann::json &j) {
nlohmann::json::json_pointer last_accessed;
LastAccessedHelper acc{j, last_accessed};
try {
parse_tree(acc.at("tree"), last_accessed);
} catch (std::exception &ex) {
throw std::runtime_error("Exception at " + last_accessed.to_string() + ": " + ex.what());
}
}
/* Will parse something like
{"tree": {
"value" : -50,
"children": [
{"value": -30, "children": []},
{"value": 20, "children": [{"value": 10, "children": []}]}
]
}}
*/它并不完美,但它适用于我的用例,而且相对较小;我认为它对有同样问题的人会很有用。我能看到的一些改进点是:
enter()/exit()?如果值得的话,可以使用std::uncaught_exceptions()来更好地支持迭代,要么添加一个助手函数,要么降低使用instantiatejson_ptr_size() function的成本
如果您看到它们,请随时提出建议/进行改进:)
https://stackoverflow.com/questions/64324002
复制相似问题