2

我正在尝试转换表单的 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;
    }
}

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

4

1 回答 1

3

缺少的是对您的 type 的序列化程序支持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>();
}

演示


random_data要处理像你的可选字段Key,你可以创建一个辅助函数,这里调用get_optional它返回 C++17 std::optional<T>。对于早期的 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;
    }
}

演示

于 2020-04-28T20:50:55.887 回答