构建嵌套 JSON

building a nested JSON

我需要读取/解析的一些数据文件具有 header 样式:

level0var = value0
level0var.level1field = value1 
level0var.level1array[11].level2field = value2
...

换句话说,它们看起来像嵌套的 C-style 结构和数组,但其中 none 在 header 中声明:我需要在阅读时推断结构。

我的计划是使用著名的 nlohmann::json 库来存储它,因为它的灵活性允许我在解析过程中更改数据的结构,并将 header 保存在更具可读性的文件中形式。

我阅读了 lhs = rhs 中的作业,它们都是字符串。给定 json header; 来处理未知的、可变深度的结构,我想做一些类似

的事情
// std::string lhs = "level0var.level1field.level2field";
// std::string rhs = "value2"; 
auto current_level = header;
while ( lhs != "" ) {
   auto between = lhs.substr ( 0, lhs.find_first_of ( "." ) );
   lhs.erase ( 0, lhs.find_first_of ( "." ) + 1 );
   if ( lhs == between ) break;
   current_level [ between ] = json ( {} );
   current_level = current_level [ between ];
}
current_level = rhs;
std::cout << std::setw(4) << header;

对于至少具有 1 个结构级别的每一行(暂时保留数组)。

奇怪的是,使用这个循环,最后一行returns唯一的事情是null,而当我使用

header [ "level0var" ] [ "level1field" ] [ "level2field" ] = rhs;
std::cout << std::setw(4) << header;

它给出了预期的结果:

{
    "level0var": {
        "level1field": {
           "level2field": "value2"
        } 
    }
}

有没有办法迭代地构建这个层次结构(而不​​是作为一个整体提供)?一旦我知道如何做结构,我希望数组会很简单!

我在工作中制作的示例没有 运行 on coliru(我猜它没有 JSON 库)。

我设法用这段代码实现了我所理解的你想要的:

using namespace nlohmann;
    
void main()
{
    json header;
    std::string lhs = "level0var.level1field.level2field";
    std::string rhs = "value2"; 
    json * current_level = &header;
    while (lhs != "") {
        auto between = lhs.substr(0, lhs.find_first_of("."));
        lhs.erase(0, lhs.find_first_of(".") + 1);
        (*current_level)[between] = json({});
        current_level = &((*current_level)[between]);
        if (lhs == between) break;
    }
    *current_level = rhs;
    std::cout << std::setw(4) << header;
}

一些注意事项:

  1. 虽然起初看起来很有用,但我还没有成功使用nlohmann::json_pointer
    相反,我使用了一个“普通”的原始指针。需要一些指针语义才能引用现有的 json 结构。

  2. 我把退出循环的条件移到了循环的末尾(不然最内层就没了)

  3. 老实说,我不确定我的解决方案是最好的。在这种情况下处理原始指针是一件必须非常小心的事情。不过喜欢的可以试试

这对于 json_pointer 来说确实很简单,使用正确的 operator[] overload and the json_pointer::get_unchecked() 函数已经为您完成了所有这些工作。

唯一的努力是将您的 . 分隔的密钥转换为它期望的 / 分隔的路径。

#include <nlohmann/json.hpp>

#include <algorithm>
#include <iostream>
#include <string>

using json = nlohmann::json;

std::string dots_to_path(std::string key)
{
    std::replace(key.begin(), key.end(), '.', '/');
    key.insert(0, 1, '/');
    return key;
}

int main() {

    json header;

    std::string lhs = "level0var.level1field.level2field";
    std::string rhs = "value1";

    json::json_pointer key{dots_to_path(lhs)};
    header[key] = rhs;
    std::cout << std::setw(4) << header;
}

为了将来参考,链接代码已扩展为转换键,包括数组索引,如 level0var.level1field[1].level2field -> level0var/level1field/1/level2field

在这个阶段,将原始字符串标记化并简单地为每个标记附加 json_pointer::operator/= 可能会更清晰,但由于它不在原始问题范围内,我将把它留作 reader.