MongoDb 二 MFC网络数据解析及存储

转载请注明出处:https://blog.csdn.net/mymottoissh/article/details/82875649

上一篇记录了MongoDb服务端和客户端的搭建过程,包括Windows C++驱动的安装,并做了简单的连接测试。这次继续上次的内容,开始将数据库在MFC中进行集成。本打算做一个简易的天气查询软件,但是考虑到实验的重点在于MongoDb的操作,所以最终软件中只包含了城市列表的查询,如果想进一步开发天气查询功能,只需在Dao基础上加些UI。

关键字 : MongoDb api 封装 MFC

简述一下软件实现的功能:启动后通过http获取天气查询网站的城市列表部分,并存储到MongoDb。点击"城市列表"按钮,显示数据库的信息。在输入框中输入城市对应编号,并点击"确定"按钮,可以删除数据库中的数据。就是这么简单的一个功能,重点在于实现MongoDb的增删改查。

先看一下效果:

效果图

代码是基于MFC库,并集成了mongoose Http框架和jsoncpp 解析框架。当然最重要的是对MongoDb的api进行了设计和封装,方便在代码中调用。

mongoose是一个轻量级的C语言Http框架。项目中仅需包含mongoose.h和mongoose.c两个文件即可。简单易用

github下载地址:https://github.com/cesanta/mongoose

jsoncpp用于解析网络传输的json数据,直接附下载地址:https://github.com/open-source-parsers/jsoncpp

源数据取自:https://www.nowapi.com/api/weather.city

虽然是个天气查询网站,但是只用了其中城市列表的部分。

环境搭建

简要说明一下环境搭建过程。

1、新建MFC项目,按照上一篇说的步骤搭建MongoDb驱动的代码环境。

2、下载mongoose,并将mongoose.c和mongoose.h分别添加到项目的源文件和头文件目录中。

3、下载jsoncpp,添加到项目的包含目录和库目录中。同时将json_reader.cpp、json_writer.cpp、json_value.cpp三个文件添加到项目源文件中,将json_bachallocator.h、json_internalarray.inl、json_internalmap.inl、json_valueiterator.inl添加到头文件中。

代码编写

首先对MongoDb相关api进行了封装(MongoManager.cpp),主要封装了初始化、增、删、改、查、统计数量等方法。这部分会在后面重点说明。

typedef void (*mongoCallback)(const char *);

class MongoManager
{
private:
	static MongoManager* mongoMgr;
	MongoManager()
	{
		conn = NULL;
		errmsg = "";
	};
	~MongoManager(){};
	MongoManager(const MongoManager& hm){};
	MongoManager& operator=(const MongoManager& hm){};
public:
	static CitylistInterface* inter;
	void setInter(CitylistInterface* inter);
	static MongoManager* Instance();
	static void Destroy();
	DBClientConnection *conn;
	string errmsg;
	int MongoInit(string url);
	int MongoCount(string ns, string q);
	int MongoInsert(string ns, string insert);
	int MongoRemove(string ns, string remove);
	int MongoUpdate(string ns, string qry, string update);
	int MongoQuery(string ns, string query);
	void MonogDestroy();
};

然后封装了http请求方法,并提供注册callback的方法。cb主要终于在收到200响应码后,处理字符串。

typedef void (*httpCallback)(const char *);
void HttpClientInit();
void HttpClientFree();
void HttpClientConn(string url, string contentType, httpCallback cb);

有了http和mongodb接口,开始进行写界面。

项目中共用到两个Dialog,第一个是主界面(WeatherInfoDlg.cpp),第二个用于城市列表的显示(DlgCityList.cpp)。主界面加载时,做了三件事。

1、初始化数据库连接。2、初始化网络连接。3、连接获取源数据,并注册回调。

//初始化数据库
MongoManager::Instance()->MongoInit(MONGO_URL);
//初始网络连接,并请求城市列表存入数据库
HttpClientInit();
HttpClientConn(URL_CITYLIST, CONTENT_TYPE, hcb);

先来看一下我们的网站返回的源数据格式,标准的json字符串。

{
	"success": "1",
	"result": {
		"1": {
			"weaid": "1",
			"citynm": "北京",
			"cityno": "beijing",
			"cityid": "101010100",
			"area_1": "北京",
			"area_2": "城区",
			"area_3": ""
		},
		"2": {
			"weaid": "2",
			"citynm": "朝阳",
			"cityno": "bjchaoyang",
            ……

所以在请求网络url时,注册的回调功能就是解析json字符串,组拼成需要的对象结构,并存入mongoDb。

void hcb(const char *cont)
{
	Json::Reader reader;
	Json::Value root;
	Json::Value res;
	Json::Value v;
	string jstr;
	stringstream mongcont;
	reader.parse(cont, root);
	string& succ = root["success"].asString();
	if(succ == "1" && MongoManager::Instance()->MongoCount(DBNAME_CITYLIST, "{}") == 0)
	{
		res = root["result"];
		Value::Members members = res.getMemberNames();
		for(Value::Members::iterator it = members.begin(); it != members.end(); it++)
		{
			v = res[*it];
			mongcont << "{" << "weaid:'" << v["weaid"].asString() << "',citynm:'" << v["citynm"].asString() << "',cityno:'" << v["cityno"].asString() << "',cityid:'" << v["cityid"].asString() << "',area_1:'" << v["area_1"].asString() << "',area_2:'" << v["area_2"].asString() <<"',area_3:'" << v["area_3"].asString() << "'}";
			jstr = mongcont.str();
			MongoManager::Instance()->MongoInsert(DBNAME_CITYLIST, jstr);
			mongcont.str("");
			mongcont.clear();
		}
	}
}

点击"城市列表",会弹出DlgCityList。同时查询数据库中的条目数和内容,并显示到新弹出的对话框中。

void CDlgCityList::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, m_list);
	DDX_Control(pDX, IDC_NUMBER, m_citynum);
	CString str[] = {TEXT("content")};
	m_list.InsertColumn(0, str[0], LVCFMT_LEFT, 100);
	m_list.SetColumnWidth(0, 1000);
	
	int cnt = MongoManager::Instance()->MongoCount(DBNAME_CITYLIST, "{}");
	CString strCont;
	strCont.Format(_T("%d"), strCont);
	m_citynum.SetWindowTextW(sss);
	MongoManager::Instance()->setInter(this);
	MongoManager::Instance()->MongoQuery(DBNAME_CITYLIST, "{}");	
}

由于CDlgCityList实现了MongoDb的回调接口,上面的代码同时注册了回调,用于对查询到的数据进行处理。

void CDlgCityList::CityListCallback(const char *s)
{
	int num = MultiByteToWideChar(CP_UTF8,0,s,-1,NULL,0);
	wchar_t *wide = new wchar_t[num];
	MultiByteToWideChar(CP_UTF8,0,s,-1,wide,num);
	m_list.InsertItem(0, wide);
}

文字输入框输入城市编号,点击"确定",则会触发MongoDB删除对应的数据项。

void CWeatherInfoDlg::OnBnClickedConfirm()
{
	// 首先获取edittext中的字符串
	CString str;
	m_edit.GetWindowTextW(str);
	// 然后删除数据库中对应项
	string cnm = CT2A(str.GetBuffer());
	string q = "{weaid:'" + cnm + "'}";
	MongoManager::Instance()->MongoRemove(DBNAME_CITYLIST, q);	
}

代码未经雕琢,知识展示了大概的设计思路。

MongoDB API

MongoDB 之 insert

命令行语法 db.collection.insert(<insert>)

API函数原型

virtual void insert(const std::string& ns, //库名.文档名
                        BSONObj obj, //插入的数据,对应json
                        int flags = 0, 
                        const WriteConcern* wc = NULL //写操作的应答级别
                    );

wc:写操作的应答级别,其实主要是定义抛出哪些异常,网上有很多,不赘述。

obj:写入数据对应的json对象,通过插入的字符串构造。

了解函数原型之后,插入数据的问题就退化为构造BSON对象的问题。共有四种方法,区别就是构造BSON的手法不同,项目中采用第四种。

//第一种 append
BSONObjBuilder b1;
b1.append("id",1);
b1.append("name", "lisi");
conn->insert(ns, b1.obj());
	
//第二种 流模式
BSONObjBuilder b2;
b2 << "id" << 2 << "name" << "wangwu";
conn->insert(ns, b2.obj());
	
//第三种 直接构造
conn->insert(ns, BSON("id" << 3 << "name" << "zhaoliu"));

//第四种 通过Query构造BSON
Query ins("{id:5, name:'xuqi'}");
conn->insert(ns, ins.obj);

MongoDB 之 remove

命令行语法 db.collection.remove(<remove>,{justOne:<boolean>,writeConcern:<document>})

API函数原型

virtual void remove(const std::string& ns, //库名.文档名
                    Query q, //删除的条件,可以用字符串直接构造
                    bool justOne = 0, //1表示只删除一条,0表示删除所有
                    const WriteConcern* wc = NULL);
Query qry("{id:1}");
Query qry(remove);
conn->remove(ns, qry);

MongoDB 之 update

命令行语法 db.collection.update(<query>, <update>, {upsert:<boolean>, multi:<boolean>, writeConcern:<document>})

API函数原型

virtual void update(const std::string& ns, //库名.文档名
                        Query query, //查询用的query对象
                        BSONObj obj, //更新用的BSON对象
                        bool upsert = false, //若为true,表示如果没有查询到对应项,则插入一条新的
                        bool multi = false, //是否更新多条数据
                        const WriteConcern* wc = NULL);
Query qry(q);
Query upt(update);
conn->update(ns, qry, upt.obj);

MongoDB 之 query

命令行语法 db.collection.find(<query>, <show>)

API函数原型

virtual std::auto_ptr<DBClientCursor> query(const std::string& ns, //库名.文档名
                                            Query query = Query(), //查询用的query
                                            int nToReturn = 0, //返回条目数
                                            int nToSkip = 0, //跳过条目数
                                            const BSONObj* fieldsToReturn = 0, //返回数据包含的字段
                                            int queryOptions = 0,
                                            int batchSize = 0);

 关于batchSize引用一段说明

Specifies the number of documents to return in each batch of the response from the MongoDB instance. In most cases, modifying the batch size will not affect the user or the application, as the mongo shell and most drivers return results as if MongoDB returned a single batch.

BSONObj obj;
auto_ptr<DBClientCursor> cursor = conn->query(ns, Query("{}"));
while(cursor->more())
{
	obj = cursor->next();
	if(NULL != inter)
	{
		inter->CityListCallback(obj.jsonString().c_str());
	}	
}

以上对主要的API进行了封装,可作为Dao层直接调用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值