【问题标题】:Error in converting from json using nlohmann json package使用 nlohmann json 包从 json 转换时出错
【发布时间】:2020-04-28 14:59:59
【问题描述】:

我正在尝试转换表单的json

{
   "content": {
     "test_key": "test"
   },
   "sender": "alice",
   "type": "key_type"
}

我的对象是

template<class Content>
struct Event
{
        Content content;
        std::string type;
};

正在使用模板,因为内容的结构不固定。当我尝试使用 from_json 时,就像

template<class Content>
void
from_json(const nlohmann::json &obj, Event<Content> &event)
{
        event.content = obj.at("content").get<Content>();
        event.type    = obj.at("type").get<std::string>();
}

我收到了错误

[json.exception.out_of_range.403] 未找到键“内容”

虽然 json 中有内容键。为什么会这样?

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using namespace std;

template<typename Content>
struct Event
{
    Content content;
    string type;
};

template<typename Content>
void from_json(const nlohmann::json &obj, Event<Content> &event)
{
    event.content = obj.at("content").get<Content>();
    event.type    = obj.at("type").get<string>();
}

struct Key{
    string test_key;
    string random_data;
};

int main(){
    json j={{"content",{{"test_key","test"}}},{"sender","alice"},{"type","key_type"}};

    Event<Key> event_instance;

    try{
        from_json(j,event_instance);
    }
    catch(json::exception& e){
        cout<<e.what()<<endl;
    }
}

上面的代码是一个最小可重现的例子

【问题讨论】:

  • @TedLyngmo 我想我会详细说明以便于理解 for_json 函数第一个参数是第一个 sn-p 中提到的 json,第二个参数是第二个 sn-p 中的类的一个实例。在将这些参数传递给函数时,我收到了这个错误。希望现在看起来没问题。

标签: c++ json nlohmann-json


【解决方案1】:

缺少的是对您的类型Key 的序列化程序支持。添加后,提取工作:

void from_json(const nlohmann::json& obj, Key& k) {
    k.test_key = obj.at("test_key").get<std::string>();
    // k.random_data missing in json
}

template<typename Content>
void from_json(const nlohmann::json& obj, Event<Content>& event) {
    event.content = obj.at("content").get<Content>();
    event.type = obj.at("type").get<std::string>();
}

Demo


要处理Key 中的random_data 等可选字段,您可以创建一个辅助函数,此处称为get_optional,它返回C++17 std::optional&lt;T&gt;。对于早期的 C++ 版本,您可以使用 boost::optional

#include <nlohmann/json.hpp>

#include <iostream>
#include <optional>
#include <string>

using json = nlohmann::json;

template<typename Content>
struct Event {
    Content content{};
    std::string type{};
};

struct Key {
    std::string test_key{};
    std::optional<std::string> random_data{}; // optional field made optional
};

template<typename T>
std::optional<T> get_optional(const json& obj, const std::string& key) try {
    return obj.at(key).get<T>();
} catch(const json::exception&) {
    return std::nullopt;
}

void from_json(const json& obj, Key& k) {
    k.test_key = obj.at("test_key").get<std::string>();
    k.random_data = get_optional<std::string>(obj, "random_data");
}

template<typename Content>
void from_json(const json& obj, Event<Content>& event) {
    event.content = obj.at("content").get<Content>();
    event.type = obj.at("type").get<std::string>();
}

int main() {
    json j = {{"content", {{"test_key", "test"}}},
              {"sender", "alice"},
              {"type", "key_type"}};

    try {
        auto event_instance = j.get<Event<Key>>();
        std::cout << event_instance.content.test_key << '\n';

        if(event_instance.content.random_data) {
            std::cout << event_instance.content.random_data.value() << '\n';
        } else {
            std::cout << "no random_data\n";
        }

        std::cout << event_instance.type << '\n';
    } catch(const json::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}

Demo

【讨论】:

  • 但我的问题是我有时可以在内容中包含 random_data 字段,在这种情况下,我应该有单独的序列化器。所以有什么办法可以一个通用的序列化器,它可以包含所有可能的字段?
  • @Bad_Panda 您的问题根本没有描述这个问题。就你的问题而言,我的回答回答了它。如果无论如何,如果您的 json 数据未将 1:1 映射到完整的 C++ 对象,则您必须添加检查以查看 json 是否包含该字段并提供默认值(如果包含) - alt。将random_data 变成std::optional&lt;std::string&gt; random_data;
  • @Bad_Panda 欢迎您。我想在我的答案中添加一个如何使用std::optional 的示例,以及如何为此创建一个辅助函数,但它会触发valgrind 中的泄漏。我会把它贴在这里,这样你就可以看到这个想法:godbolt.org/z/DkAjqD
  • @Bad_Panda 似乎valgrind 报告的泄漏只发生在使用 LLVM 的libc++ 并且在get_optional() 中抛出异常时。使用默认的 libstdc++g++clang++ 不会报告泄漏 - 所以我现在添加了示例。
  • 谢谢,这真是太棒了。使用 std::optional 和辅助函数在交换 json 时提供了更大的灵活性。
猜你喜欢
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多