C++结构体和JSON字符串之间的相互转换

时间:2025-03-31 09:01:27

搞了份代码,在VS2013下运行通过。

有的时候,我们需要对C++的结构体进行JSON化,有的时候,我们又需要把JSON字符串转换成C++结构体,先看下面的代码

struct TestStruct
{
	string strMember1;
	float fMember2;
	double fMember3;
};



TOJSON(TestStruct, v.strMember1, v.fMember2);
void main()
{
      //下面代码是将C++结构体转换成JSON
	TestStruct test1;
	test1.strMember1 = "I am member1";
	test1.fMember2 = 2.0f;
	test1.fMember3 = 3.0;
	string_stream ss;
	save_to(ss, test1);
	cout << () << "\n" << endl;


      //下面代码是将JSON转换成C++结构体
	std::string strJson = "\{\"strMember1\":\"I am json member 1\",\"fMember2\":-2\}";
	TestStruct test2;
	load_from_buff(test2, (char *)strJson.c_str());
	int kk = 0;
	std::cin >> kk;
}

这段代码会在屏幕上显示{"strMember1":"I am member1","fMember2":2}     如果你在最后一行打个断点。观察test2的值,将会发现其第一个成员的值是

“I am json member" 第二个成员的值是-2  

这是怎么实现的呢?这需要使用C++的模板类

先看json转换成C++结构体 load_from_buf的代码如下

template<typename ty>
    inline void load_from_buff(ty& val, char * buff, size_t len = -1)
    {
        reader rd(buff, len);
        json_impl<ty>::read(rd, val);
    }

reader类只是提供一些帮助解析的函数。

json_impl负责具体的干活。先来看一下。浮点数的成员变量如何处理。代码如下


 template<typename ty, class enable = void>
    struct json_impl;

template<typename ty>
    struct json_impl < ty,
        typename std::enable_if <std::is_floating_point<ty>::value>::type >
    {
        static inline void read(reader& rd, ty& val)
        {
            auto& tok = ();
            switch ()
            {
            case token::t_string:
            {
                                    double temp = std::strtold(, nullptr);
                                    val = static_cast<ty>(temp);
                                    break;
            }
            case token::t_int:
            {
                                 val = static_cast<ty>(.i64);
                                 break;
            }
            case token::t_uint:
            {
                                  val = static_cast<ty>(.u64);
                                  break;
            }
            case token::t_number:
            {
                                    val = static_cast<ty>(.d64);
                                    break;
            }
            default:
            {
                       ("not a valid float point number.");
            }
            }
            ();
        }
        template<typename write_ty>
        static inline void write(write_ty& wt, ty const& val)
        {
            char buffer[64] = { 0 };
#ifdef _MSC_VER
            _gcvt_s(buffer, 63, val, 8);
#else
            gcvt(val, 62, buffer);
#endif // MSVC
            size_t len = std::strlen(buffer);
            if (buffer[len - 1] == '.')
            {
                buffer[len - 1] = '\0';
                --len;
            }
            wt.write_liter(buffer, len);
        }

        template<typename write_ty>
        static inline void write_key(write_ty& wt, ty const& val)
        {
            ('"');
            write<write_ty>(wt, val);
            ('"');
        }
    };

接着就是最关键的宏,依次读出结构体的成员名字


#define TOJSON(TYPE,...) \
namespace ajson{\
    template<> \
struct json_impl < TYPE, void > \
{ \
    static inline detail::filed_list& this_filed_list() \
{\
    static auto fields = detail::split_fields(STRINGFY_LIST(__VA_ARGS__)); \
    return fields; \
}\
    static inline void read(reader& rd, TYPE& v) \
{ \
    auto& fields = this_filed_list(); \
if (('{') == false){ ("read object must start with {!"); } \
    (); \
if (('}')) \
    return; \
    auto mber = (); \
    size_t pos = 0; \
do \
{ \
if ( != token::t_string){ ("object key must be string"); } \
    (); \
if ((':') == false){ ("invalid json document!"); } \
    (); \
if (read_members(rd, &fields[0], , 0, __VA_ARGS__) == 0) \
{ \
    skip(rd); \
} \
if (('}')) \
{ \
    (); \
    return; \
} \
      else if ((',')) \
{ \
    (); \
    mber = (); \
    continue; \
} \
    ("invalid json document!"); \
} while (true); \
} \
    template<typename write_ty>\
    static inline void write(write_ty& wt, TYPE const& v)\
{\
    auto& fields = this_filed_list(); \
    ('{'); \
    ::ajson::write_members(wt, &fields[0], 0, __VA_ARGS__); \
    ('}'); \
}\
}; \
}


整个原理就是这个样子了。

当然宏定义也可以放在结构体的内部,但是这样就不得不对结构体进行改变,不过这样的好处是可以检测是否有遗漏的结构体项,在特殊需求下可以使用。另外如果不想改变现有的任何代码,可以使用PDB文件。PDB文件里当然有所有类成员的名字,坏处就是无法进行编译检查(/lumpyzhu/nmscc的观点,我只是引用)


有了整个原理,相信大家都不难实现出完整的代码。(有兴趣的同学可以每个方案都实现一下)。

如果对实现的原理还有任何疑问,欢迎交流探讨!