C++ 转换成 JSON
经常有朋友问我如何将C++对象转换成JSON格式字符串。我的回答通常是CppCMS中的json::value. 我也写过一篇文章介绍该技术。
但是最近有些不同的想法。因为用到一个vector<shared_ptr<> > 数据结构,json::value不支持这个模板特化。同时也发现json::value的设计思想是将所有的c++对象存储在std::map中,然后再导出为json字符串。但是在我开发的和看到的很多web service开发中,将对象转换成json字符串,是非常频繁的。在esri,对象非常多,json格式巨大。如果每次都缓存到map中,显然过多的copy,内存和cpu都消耗过大。
我的一贯用法就是用流来拼接字符串。这种方法又有点傻。受json::value的设计采用模板特化和偏特化的启发。我自己也写了一个简单的jsoner类。方便了自己开发,现在拿出来。看看能不能方便别人。
先看调用代码:
vector<string> emails = user_manager::get_emails(user_id); content.user_emails = jsoner<vector<string> >::to_json("emails", emails); shared_ptr<displays> ds = user_manager::find_displays(user_id); content.user_displays_addresses = jsoner<displays>::to_json("displays", *ds);第一个例子:
第一代码从mongodb中读取用户所有的email地址,放到vector<string>容器中。第二行代码将之导出为固定格式的json字符串。这是第一个例子,简单但很常用。cppcms::json::value不支持,转换会失败。
第二个例子:
第三行代码找到用户所有拥有的设备(复数),displays内部拥有一个vector<shared_ptr<display> >成员变量,display就是一个显示设备,拥有一些属性。这个例子比较复杂,但是也很常用。良好的封装经常用复数类包装,将底层容器的类型对外部屏蔽,这里你不知道我用的是vector,以后我还可以换成list。
为了支持上面的调用代码,我的jsoner类设计了三个模板重载形式。
#ifndef __JSON_HELPER_H #define __JSON_HELPER_H #include <time.h> #include <string> #include <vector> #include <sstream> #include <boost/shared_ptr.hpp> template<typename T> class jsoner { public: static std::string to_json (std::string const& name, T const& value) { std::stringstream stream; stream << "{\"" << name << "\":"; stream << value.to_json(); stream << "}"; return stream.str(); } }; template<> class jsoner<std::vector<std::string> > { public: static std::string to_json (std::string const& name, std::vector<std::string> const & value) { std::vector<std::string>::const_iterator itor, last = value.end(); std::stringstream stream; stream << "{\"" << name << "\":["; int i = 0; for (itor = value.begin(); itor != last; ++itor) { stream << "{"; stream << "\"index\":" << "\"" << i << "\","; stream << "\"value\":" << "\"" << *itor << "\""; stream << "}"; if(itor != last -1) { stream << ","; } ++i; } stream << "]}"; return stream.str(); } }; template<typename T> class jsoner<std::vector<boost::shared_ptr<T> > > { public: static std::string to_json (std::string const& name, std::vector<boost::shared_ptr<T> > const & value) { typename std::vector<boost::shared_ptr<T> >::const_iterator itor, last = value.end(); std::stringstream stream; stream << "{\"" << name << "\":["; int i = 0; for (itor = value.begin(); itor != last; ++itor) { stream << "{"; stream << "\"index\":" << "\"" << i << "\","; stream << "\"value\":" << (*itor)->to_json(); stream << "}"; if(itor != last -1) { stream << ","; } ++i; } stream << "]}"; return stream.str(); } }; #endif
第一种形式是最普通的,要求T类型必须拥有to_json成员函数。
第二种形式用于支持vector<string>,是模板特化。
第三种形式也是一种特化,但是支持vector<shared_ptr<T>>参数。
在我刚才的第二个例子调用代码中,找到的是第一种形式用于displays,displays的to_json函数代码如下:
string displays::to_json() const { return jsoner<vector<shared_ptr<display> > >::to_json("addresses", values_); }它内部调用了第三种形式的特化。而这种形式又要求display类拥有成员函数to_json。
In short,如果你自己的类想要使用这几个模板,都必须实现自己的to_json函数。
参考这种思路,可以扩充到自己需要的stl容器,比如list等。
转自:http://blog.csdn.net/sheismylife/article/details/7958470