将 C++ 类转换为 JSON

Posted

技术标签:

【中文标题】将 C++ 类转换为 JSON【英文标题】:Converting C++ class to JSON 【发布时间】:2012-01-03 10:37:51 【问题描述】:

我想创建一个包含我的类的实例变量的 JSON 字符串。

例如,

class Example   
    std::string string;  
    std::map<std::string, std:string> map;  
    std::vector<int> vector;  
;

会变成:


    "string":"the-string-value",
    "map": 
        "key1":"val1",
        "key2":"val2"
    ,
    "vector":[1,2,3,4]

我研究了几个用于创建 JSON 的 C++ 库,它们看起来都非常复杂。我想要类似于 javascriptJSON.stringify(object) 的东西。换句话说,只需将 std::map 传递给它并接收一个字符串。该映射可以包含其他映射、向量、列表、字符串、数字和布尔值。

最好的方法是什么?

感谢您的帮助。

编辑

我研究了以下内容:

json精神、jsoncpp、zoolib、JOST、CAJUN、libjson、nosjob、JsonBox、jsonme--

据我所知,我可以在下面的答案中构造一个单独的 JSON 对象并转换为 JSON 我希望能够将我的东西存储在标准集合中并进行转换。

编辑 2

好的,放弃对类进行序列化的想法,因为 C++ 缺乏反射,这似乎是不可能的。

有没有一种很好的方法可以将包含 std:maps、std::vectors、std::lists、数字、字符串和布尔值的 std::map 转换为 JSON,而无需更改数据类型或将数据复制到新的数据类型?

谢谢。

【问题讨论】:

你看过哪些库?所以我们知道什么你觉得复杂 参见 ***.com/questions/245973/whats-the-best-c-json-parser 和 ***.com/questions/6538725/…(现已删除;仅 10k+) 我已经更新了我的帖子。复杂性在于需要做很多工作才能做一些我认为非常简单的事情。我真的觉得我错过了一些东西,可能是一些明显的东西。 你没有遗漏任何东西,这在 C++ 中是不可能的(以你描述的形式)。 您可能会以错误的方式处理此问题。这样的“仅数据”类可能应该是std::tuple;并且您应该能够很容易地构建一些模板操作来输出元组的 JSON 表示。 【参考方案1】:

这是一个解决方案,允许您使用现有的类/结构,而无需在自定义字典中手动创建和维护您的键和值。它使用 nlohmann json。

nlohmann::json 允许您进行任意类型转换。这里https://nlohmann.github.io/json/features/arbitrary_types/有更详细的描述。

代码:

// example.cc
#include <string>
#include <iostream>
#include <map>
#include <vector>

#include "third_party/nlohmann/json.hpp"

struct Example 
    std::string name;
    std::map<std::string, int> dict;
    std::vector<int> vec;
    std::string this_member_is_not_part_of_the_json;
;

// Use NLOHMANN_DEFINE_TYPE_INTRUSIVE if your members are private.
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example,
    name,
    dict,
    vec);

int main() 
  Example example;
  example.name = "Example";
  example.dict = "AnyKey", 42;
  example.vec = 1, 2, 3;

  nlohmann::json json;
  to_json(json, example);  // "generated" function

  std::cout << json.dump(1) << std::endl;

  return 0;

编译代码:

g++ -I third_party/nlohmann/ example.cc -o example

运行./example 输出:


 "dict": 
  "AnyKey": 42
 ,
 "name": "Example",
 "vec": [
  1,
  2,
  3
 ]

解释:

注意我如何在结构定义之外使用NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE,这会在同一命名空间中生成函数to_json(...)

json.dump(1) 将 json 转换为格式化字符串。

【讨论】:

【参考方案2】:

我是https://github.com/beached/daw_json_link 的作者。你是对的,C++ 目前没有反射,如果能把简单的东西排除在外,那会很好。但是通过定义一个简单的声明式映射,JSON Link 将为您的类型提供一个类型检查的解析器。例如,您指定的类可以映射为:

    class Example   
      std::string string;  
      std::map<std::string, std:string> map;  
      std::vector<int> vector;  
    ;
        
    namespace daw::json 
      template<> 
      struct json_data_contract<Example> 
        using type = json_member_list<
          json_link<"string", std::string>, 
          json_link<"map", std::map<std::string, std::string>>
          json_link<"vector", std::vector<int>>>;
        
        static inline auto to_json_data( Example const & v ) 
          return std::forward_as_tuple( v.string, v.map, v.vector );
        
      ;
    

从这里你可以在另一个内部使用这个映射作为json_class&lt;"Name", Example&gt;。按照您的要求进行序列化只是auto json_document = daw::json::to_json( MyExampleValue ) 或解析它daw::json::from_json&lt;Example&gt;( json_document ); 的问题。该库的美妙之处在于它为您的类型生成自定义解析器,并在解析时对数据进行类型检查。

【讨论】:

【参考方案3】:

在RareCpp 中,我在反射实现之上创建了一个非常有效的JSON Library。它是为 C++17 编写的,可与 Visual Studios、g++ 和 Clang 一起使用。该库仅是标头,这意味着您只需将反射和 json 标头复制到您的项目中即可使用它。

JSON 库只要求您在 REFLECT 宏中列出一次字段;从那里它自动识别用于读取或写入的适当 JSON 输出表示,并且可以递归地遍历任何反射字段,以及数组、STL 容器、引用、指针等。

struct MyOtherObject  int myOtherInt; REFLECT(MyOtherObject, myOtherInt) ;
struct MyObject

    int myInt;
    std::string myString;
    MyOtherObject myOtherObject;
    std::vector<int> myIntCollection;

    REFLECT(MyObject, myInt, myString, myOtherObject, myIntCollection)
;

int main()

    MyObject myObject = ;
    std::cout << "Enter MyObject:" << std::endl;
    std::cin >> Json::in(myObject);
    std::cout << std::endl << std::endl << "You entered:" << std::endl;
    std::cout << Json::pretty(myObject);

上面可以这样运行...

Enter MyObject:

  "myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
  "myOtherObject": 
    "myOtherInt": 9001
  



You entered:

  "myInt": 1337,
  "myString": "stringy",
  "myOtherObject": 
    "myOtherInt": 9001
  ,
  "myIntCollection": [ 2, 4, 6 ]

您还可以对字段进行注释以执行诸如在 JSON 表示中给它们一个不同的名称、强制它们为字符串等操作。

struct Point

    NOTE(latitude, Json::Name"lat")
    double latitude;

    NOTE(longitude, Json::Name"long")
    double longitude;

    REFLECT(Point, latitude, longitude)
;

更多示例请参见here,还有许多其他功能,例如捕获超类、支持读取、遍历和写入编译时未知的 JSON,进一步自定义特定字段或类型的 JSON 表示等。

【讨论】:

如何将其添加到 VisualStudio 2019 项目中? 只需将github.com/TheNitesWhoSay/RareCpp/blob/master/RareCppLib/… 和github.com/TheNitesWhoSay/RareCpp/blob/master/RareCppLib/Json.h heders 添加到您的项目中,并在需要的地方包含它们;还可以右键单击您的项目 -> 属性并在“常规”下确保 C++ 语言标准设置为 C++17 或更高版本【参考方案4】:

你可以使用Boost.PropertyTree。

#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

namespace pt = boost::property_tree;

int main() 
    // Create an empty property tree object.
    pt::ptree tree;

    // Put a string value into the tree.
    tree.put("string", "the-string-value");

    // Put a map object into the tree.
    pt::ptree child1;
    std::map<std::string, std::string> map = "key1", "val1",
                                              "key2", "val2";
    for (auto &p : map) 
        child1.add(p.first, p.second);
    
    tree.add_child("map", child1);

    // Put a vector of numbers into the tree
    pt::ptree child2;
    std::vector<int> vector = 1, 2, 3, 4;
    for (auto &v : vector) 
        pt::ptree item;
        item.put("", v);
        child2.push_back(std::make_pair("", item));
    
    tree.add_child("vector", child2);

    // Write property tree to JSON file
    pt::write_json("output.json", tree);

    return 0;

输出:


    "string": "the-string-value",
    "map": 
        "key1": "val1",
        "key2": "val2"
    ,
    "vector": [
        "1",
        "2",
        "3",
        "4"
    ]

【讨论】:

【参考方案5】:

JSON Spirit 允许您这样做:

Object addr_obj;

addr_obj.push_back( Pair( "house_number", 42 ) );
addr_obj.push_back( Pair( "road",         "East Street" ) );
addr_obj.push_back( Pair( "town",         "Newtown" ) );

ofstream os( "address.txt" );
os.write( addr_obj, os, pretty_print );
os.close();

输出:


    "house_number" : 42,
    "road" : "East Street",
    "town" : "Newtown"

我想json_map_demo.cpp 将是一个不错的起点。

【讨论】:

我希望能够做到这一点,而不必将每个值都复制到单独的 JSON 对象中。 @Ca1icoJack:你不必复制。您可以调整您现有的结构。正如 Tamás 所指出的,这需要工作,因为 C++ 没有反射 您是否介意详细说明 adapt 的含义。您的意思是使用 JSON Spirit Object 而不是 std::map?我是 C++ 新手,习惯了几种必须使用反射的语言(我目前正在阅读)。谢谢。【参考方案6】:

如果问题仍然存在,请查看json_dto 库,这是一个仅用于在 JSON 表示和 c++ 结构之间转换数据的小型标头助手。

例如具有以下结构:

struct message_source_t

  // Worker thread.
  std::int32_t m_thread_id;

  // Sender.
  std::string m_subsystem;
;

struct message_t

  // Who sent a message.
  message_source_t m_from;

  // When the message was sent (unixtime).
  std::tm m_when;

  // Message text.
  std::string m_text;
;

json_dto 的帮助下,您可以创建以下 JSON:


  "from" : 
    
      "thread_id" : 4242,
      "sybsystem" : "json_dto"
    ,
  "when" : "2016.09.28 19:55:00",
  "text" : "Hello world!"
  

并且给定这样的 JSON 字符串,您可以将其转换为结构。

【讨论】:

别忘了你必须实现 json_io 成员函数,你将结构成员映射到 JSON 中的成员。这是由于 C++ 中缺乏反射。【参考方案7】:

您想对地图或对象进行 JSON 化吗? (您的示例显示了一个类,但您说的是地图)。如需地图,请查看此库 - JSON Spirit。

对于对象:C++ 中没有反射支持(除了非常有限的 RTTI),因此也没有序列化的“一键式”解决方案。任何解决方案都需要您为要序列化和反序列化的类编写额外的、可能紧密耦合的代码(这取决于您是否要序列化非公共数据)。

【讨论】:

很抱歉给您带来了困惑。最好我想对 JSON 做对象,但构建地图并存储它是另一种选择。我一生都无法弄清楚如何使用 JSON Spirit 映射到 JSON。它适用于 JSON 的向量/列表。 这个答案是正确的,C++ 永远不会有像 Jackson 这样的库,可以在不编写与指定类紧密耦合的代码的情况下将对象转换为 JSON。【参考方案8】:

任何好的 C++ JSON 库都应该这样做,但很遗憾他们没有这样做——除了ThorsSerializer 和显然是Nosjob,正如question 中提到的那样。

当然,C++ 不像 Java 那样具有反射,因此您必须显式注释您的类型:(从 ThorsSerializer 文档中复制)

#include "ThorSerialize/JsonThor.h"
#include "ThorSerialize/SerUtil.h"
#include <map>
#include <vector>
#include <string>
#include <iostream>

class Example 
    std::string string;
    std::map<std::string, std::string> map;
    std::vector<int> vector;

    // Allow access to the class by the serialization library.
    friend class ThorsAnvil::Serialize::Traits<Example>;

    public:
        Example(std::string const& s, std::map<std::string, std::string> const& m, std::vector<int> const& v)
            : string(s), map(m), vector(v)
        
;

// Define what members need to be serilizable
ThorsAnvil_MakeTrait(Example, string, map, vector);

示例用法:

int main()

    using ThorsAnvil::Serialize::jsonExport;
    using ThorsAnvil::Serialize::jsonImport;


    Example     e1 "Some Text", "ace", "the best", "king", "second best", 1 ,2 ,3, 4;

    // Simply serialize object to json using a stream.
    std::cout << jsonExport(e1) << "\n";

    // Deserialize json text from a stream into object.
    std::cin  >> jsonImport(e1);

跑步:


    "string": "Some Text",
    "map":
    
        "ace": "the best",
        "king": "second best"
    ,
    "vector": [ 1, 2, 3, 4]

在 C++ 中你不能做得比这更好。

【讨论】:

对于其他人:如果您像我一样是新手,请不要尝试这个库。使用起来很糟糕。我花了将近 4 个小时尝试运行这个简单的示例,但没有任何效果。即使在作者给出的简单示例中,也缺少声明。我用新安装的 Ubuntu 尝试了自制软件,但在 brew install xxx 期间失败了。然后我尝试下载仅标头分支并成功编译,但链接出错。最后我尝试下载整个 repo 并进行安装,但在 .configure 部分,我无法以简单的方式安装所有依赖项。 我最终放弃了,决定使用另一种语言来完成我的任务。如果有人可以编写有关如何使其运行的分步教程。请告诉我。 @Rick 如果您仍然关心,我是作者,非常乐意提供帮助。在此处添加问题:github.com/Loki-Astari/ThorsSerializer/issues @MartinYork 谢谢马丁。当我学习 C++ 中的反射或相关的东西时,我可能会回到这个:P。现在我主要在 Python 上工作。【参考方案9】:

你看过麦片 (http://uscilab.github.io/cereal/) 吗?它有 JSON 档案,用于使用 C++ 与 JSON 进行序列化。

可以在 SO 上找到开销最小的示例(来自谷物):https://***.com/a/22587527/255635

【讨论】:

虽然这在理论上可以回答问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。 好吧,您只需通过链接前往麦片粥即可快速找到答案。不需要为此投票,真的吗?但是很好,更新了另一个类似问题的链接,但与谷物有关。 不,我没有投反对票。一定有人将您的帖子标记为 VLQ,因此编辑会自动触发反对票。如果我当天还有选票,我也会投票 真的很难得罪我。一些改进。 1. 不要以你看过...开头,你是在回复帖子而不是要求澄清。 2.添加另一个答案的示例(或自己制作一个),并在底部添加链接以供参考。 (如果你这样做,我一定会回来为答案投票) @罗伯特。我赞成弥补谁反对你的答案。明天我将研究谷物和示例,看看它是否对我有用。谢谢。【参考方案10】:

thispython 脚本生成 c++ pod 类,每个 json 属性都有一个成员

你想要完全相反的东西,但是生成一个既可以加载又可以保存的映射类是微不足道的

生成的代码依赖于外部 json 解析器库

【讨论】:

【参考方案11】:

我已经编写了一个实验库,可以完成这项工作,但它需要对类结构和层次结构进行外部描述。它使用GCCXML构建一个xml字典,用于序列化反序列化:

http://code.google.com/p/cjson/

目前是一个实验项目,可以处理基本类型(int、float double)、指向基本类型的指针、类、继承的成员等......它实现了基本的 std::vector 和 std::map 序列化,还有 std::string 实例。

具体实现见here

【讨论】:

【参考方案12】:

我写了一个库来解决你的问题。 但是,这是一个非常新的项目,还不够稳定。 随便看看,主页在这里::

https://github.com/Mizuchi/acml

在您的示例中,您必须像这样添加一行:

ACML_REGISTER(Example, ,(string)(map)(vector));

为了告诉库您要转储哪个成员。 由于 C++ 没有反射。 而且您必须提供访问会员的方法, 使用公共成员级别或使用朋友类。

然后你只需要这样做:

字符串结果 = acml::json::dumps(any_object);

会变成::


    "string": "the-string-value",
    "map":
    
        "key1": "val1",
        "key2": "val2"
    ,
    "vector":
    
        "type": "std::vector",
        "size": "4",
        "0": "1",
        "1": "2",
        "2": "3",
        "3": "4"
    

如您所见,JSON 数组尚未实现。 现在一切都变成了字符串。

【讨论】:

@Gelldur 我没有计划支持 JSON 数组。由于 ACML 非常小(而且很干净!?),您可以自己实现此功能,欢迎您的贡献。

以上是关于将 C++ 类转换为 JSON的主要内容,如果未能解决你的问题,请参考以下文章

springmvc 枚举类转json

C# 实体类转json数据过滤掉字段为null的字段

使用Gson将对象类转成Json对象时出现u003d的问题

java net.sf.json 如何将javabean的首字母转换为大写

将 C++ 类转换为 JSON

Apifox 实体类转请求参数数据模型