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

搞了份代码,在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 << ss.str() << "\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
    inline void load_from_buff(ty& val, char * buff, size_t len = -1)
    {
        reader rd(buff, len);
        json_impl::read(rd, val);
    }

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

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


 template
    struct json_impl;

template
    struct json_impl < ty,
        typename std::enable_if ::value>::type >
    {
        static inline void read(reader& rd, ty& val)
        {
            auto& tok = rd.peek();
            switch (tok.type)
            {
            case token::t_string:
            {
                                    double temp = std::strtold(tok.str.str, nullptr);
                                    val = static_cast(temp);
                                    break;
            }
            case token::t_int:
            {
                                 val = static_cast(tok.value.i64);
                                 break;
            }
            case token::t_uint:
            {
                                  val = static_cast(tok.value.u64);
                                  break;
            }
            case token::t_number:
            {
                                    val = static_cast(tok.value.d64);
                                    break;
            }
            default:
            {
                       rd.error("not a valid float point number.");
            }
            }
            rd.next();
        }
        template
        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
        static inline void write_key(write_ty& wt, ty const& val)
        {
            wt.putc('"');
            write(wt, val);
            wt.putc('"');
        }
    };

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


#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 (rd.expect('{') == false){ rd.error("read object must start with {!"); } \
    rd.next(); \
if (rd.expect('}')) \
    return; \
    auto mber = rd.peek(); \
    size_t pos = 0; \
do \
{ \
if (mber.type != token::t_string){ rd.error("object key must be string"); } \
    rd.next(); \
if (rd.expect(':') == false){ rd.error("invalid json document!"); } \
    rd.next(); \
if (read_members(rd, &fields[0], mber.str, 0, __VA_ARGS__) == 0) \
{ \
    skip(rd); \
} \
if (rd.expect('}')) \
{ \
    rd.next(); \
    return; \
} \
      else if (rd.expect(',')) \
{ \
    rd.next(); \
    mber = rd.peek(); \
    continue; \
} \
    rd.error("invalid json document!"); \
} while (true); \
} \
    template\
    static inline void write(write_ty& wt, TYPE const& v)\
{\
    auto& fields = this_filed_list(); \
    wt.putc('{'); \
    ::ajson::write_members(wt, &fields[0], 0, __VA_ARGS__); \
    wt.putc('}'); \
}\
}; \
}


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

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


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

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



你可能感兴趣的:(其他,c++)