将C ++类转换为JSON

时间:2011-11-21 23:43:11

标签: c++ json serialization

我想创建一个包含我类的实例变量的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 ++库,它们看起来都非常复杂。我想要类似于Javascript的JSON.stringify(object)。换句话说,只需将std :: map传递给它并接收一个字符串。地图可以包含其他地图,矢量,列表,字符串,数字和布尔。

最好的方法是什么?

感谢您的帮助。

修改

我调查了以下内容:

json spirit,jsoncpp,zoolib,JOST,CAJUN,libjson,nosjob,JsonBox,jsonme -

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

修改2

好吧,废弃了序列化类的想法,因为看起来C ++缺乏反射是不可能的。

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

感谢。

12 个答案:

答案 0 :(得分:24)

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将是一个不错的起点。

答案 1 :(得分:16)

任何好的C ++ JSON库应该这样做,这是悲伤地看到,他们并不 - 与除外ThorsSerializer和{显然{3}}如在此所提及Nosjob

当然,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 ++中做得更好。

答案 2 :(得分:4)

你想要JSON-ify地图或对象吗? (您的示例显示了一个类,但您说的是一个地图)。对于地图,请查看此库 - JSON Spirit

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

答案 3 :(得分:4)

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

https://github.com/Mizuchi/acml

在您的示例中,您必须添加如下所示的一行:

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

以告诉库您要转储哪个成员。 因为C ++没有反思。 你必须提供一种方式来访问该成员, 使用公共会员级别或使用朋友级。

以后你只需要这样做:

string result = 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数组尚未实现。 现在一切都成了串。

答案 4 :(得分:2)

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

可以在SO:https://stackoverflow.com/a/22587527/255635

上找到最小开销(来自谷物)的示例

答案 5 :(得分:1)

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

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

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

查看有关实施的详细信息here

答案 6 :(得分:1)

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

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

    // Add this to your code.
    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Example, string, map, vector);
};

由于类成员是私有的,因此可以使用宏:NLOHMANN_DEFINE_TYPE_INTRUSIVE(...)。我不知道它是否适用于std::mapstd::vector,但它适用于原始类型。

就像这里:

struct MyStruct {
    std::string name;
    int number;

    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MyStruct, name, number);
};

请注意我如何使用NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE,因为结构成员是公共的。

现在我可以像这样调用to_json(Json& j, const MyStruct& s)函数了:

MyStruct s;
s.name = "test";
s.number = 11;

nlohmann::json j;
s.to_json(j, s);
std::cout << j << std::endl;
// {"name":"test","number":11}

答案 7 :(得分:0)

您可以使用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"
    ]
}

答案 8 :(得分:0)

RareCpp中,我在反射实现的基础上创建了一个非常有效的JSON Library。它是为C ++ 17编写的,可与Visual Studios,g ++和Clang一起使用。该库仅是标头,这意味着您只需要将reflect和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表示,以及更多。

答案 9 :(得分:0)

我是 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_string<"string">, 
          json_key_value<"map", std::map<std::string, std::string>, std::string>
          json_array<"vector", int>>;
        
        static inline auto to_json_data( Example const & v ) {
          return std::forward_as_tuple( v.string, v.map, v.vector );
        }
      };
    }

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

答案 10 :(得分:-1)

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

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

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

答案 11 :(得分:-1)

如果问题仍然存在,那么请查看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字符串,您可以将其转换为结构体。