2.5 半结构化消息解析器接口及其实现
现在来看如何解析XML消息和二进制消息。
XMLMessageParser和BinMessageParser是接口HalfStructuredMessageParser的实现类,它们的关系如下图所示:
图2-5 半结构化消息解析器类层次结构
半结构化消息解析器接口HalfStructuredMessageParser定义如下:
class HalfStructuredMessageParser { public: virtual HalfStructuredMessageSmartPtr / Parse(const ServiceLayerMessage& originalMsg) = 0; }; |
该接口只是将业务层消息(XML文本字符串、二进制字节流或者其他格式的消息)解析为半结构化消息。下面来看它们的两种实现。
2.5.1 XML消息解析器
XMLMessageParser定义如下:
class XMLMessageParser : public HalfStructuredMessageParser { DECLARE_STATIC_SINGLETON(XMLMessageParser) public: virtual HalfStructuredMessageSmartPtr / Parse(const ServiceLayerMessage& originalMsg); private: XMLFileParser m_parser; // 内含XML DOM解析器对象 }; #ifdef _USE_XML_MESSAGE_FORMAT_ typedef XMLMessageParser MessageParserFactory; #endif // _USE_XML_MESSAGE_FORMAT_ |
XML消息的解析需要一个XML文档解析库,例如MSXML Parser、XMLLite或者Apache Xerces-C++ Parser等。针对普通的XML消息应用,本框架对Apache Xerces-C++ Parser库的解析器和文档生成器做了简单的封装,当然底层的XML解析库可以替换为MSXML Parser、XMLLite或者其他的XML解析库产品。这个封装库的实现可参考附录指出的源代码,这里只需要这个封装库的调用接口。
其实现如下所示:
HalfStructuredMessageSmartPtr XMLMessageParser::Parse(const ServiceLayerMessage& originalMsg) { // 首先将XML消息解析为DOM树 DOMDocumentSmartPtr pDocument = / m_parser.convert_to_dom(originalMsg); if (pDocument == NULL) return HalfStructuredMessageSmartPtr(); HalfStructuredMessageSmartPtr pResultObject( / new XMLHalfStructuredMessage()); MessageHeaderSmartPtr pMessageHeader(new MessageHeader); // 获取根元素<msg> const XMLElement *pRootElement = / XMLElement::safe_cast(pDocument->getDocumentElement()); if (pRootElement == NULL || pRootElement->get_element_name() != XMLRootElement_msg::name()) return HalfStructuredMessageSmartPtr(); // 获取消息头元素<Header> const XMLElement *pHeaderElement = / pRootElement->get_sub_element(XMLSubElement_Header::name()); if (pHeaderElement == NULL) return HalfStructuredMessageSmartPtr(); // 获取消息类型元素<type> const XMLLeaf *pTypeLeaf = / XMLLeaf::safe_cast(pHeaderElement-> get_sub_element(XMLSubElement_type::name())); if (pTypeLeaf == NULL) return HalfStructuredMessageSmartPtr(); pMessageHeader->SetMessageType(pTypeLeaf->get_value()); // 获取源客户端ID元素<source> const XMLLeaf *pSourceLeaf = / XMLLeaf::safe_cast(pHeaderElement-> get_sub_element(XMLSubElement_source::name())); if (pSourceLeaf == NULL) return HalfStructuredMessageSmartPtr(); pMessageHeader->SetSource(ConvertToLong(pSourceLeaf->get_value())); // 获取目的客户端ID元素<dest> const XMLLeaf *pDestLeaf = / XMLLeaf::safe_cast(pHeaderElement-> get_sub_element(XMLSubElement_dest::name())); if (pDestLeaf == NULL) return HalfStructuredMessageSmartPtr(); pMessageHeader->SetDestination(ConvertToLong(pDestLeaf->get_value())); // 获取消息序列号元素<sn> const XMLLeaf *pSnLeaf = / XMLLeaf::safe_cast(pHeaderElement-> get_sub_element(XMLSubElement_sn::name())); if (pSnLeaf == NULL) return HalfStructuredMessageSmartPtr(); pMessageHeader->SetSn((SerialNumber) / ConvertToULong(pSnLeaf->get_value())); // 获取消息ID元素<id> const XMLLeaf *pIdLeaf = / XMLLeaf::safe_cast(pHeaderElement-> get_sub_element(XMLSubElement_id::name())); if (pIdLeaf == NULL) return HalfStructuredMessageSmartPtr(); pMessageHeader->SetMessageID(pIdLeaf->get_value());
// 完成消息头的解析 pResultObject->m_msgHeader = pMessageHeader;
// 获得消息体的根元素 const XMLElement *pBodyElement = / pRootElement->get_sub_element(XMLSubElement_Body::name()); if (pBodyElement && pBodyElement->hasChildNodes()) // has body! { DOMNodeList *pBodyNodeList = pBodyElement->getChildNodes(); // 导入消息体,并保持DOM树不变 for (XMLSize_t index = 0; index < pBodyNodeList->getLength(); ++index) { DOMNode *pNode = pBodyNodeList->item(index); if (pNode->getNodeType() == DOMNode::ELEMENT_NODE) pResultObject->m_msgBody.ImportRootElement( (DOMElement*)pNode); } } return (pResultObject); } |
2.5.2 二进制消息解析器
BinMessageParser定义如下:
class BinMessageParser : public HalfStructuredMessageParser { DECLARE_STATIC_SINGLETON(BinMessageParser) public: virtual HalfStructuredMessageSmartPtr / Parse(const ServiceLayerMessage& originalMsg); }; #ifdef _USE_BINARY_MESSAGE_FORMAT_ typedef BinMessageParser MessageParserFactory; #endif // _USE_BINARY_MESSAGE_FORMAT_ |
二进制格式的消息解析需要将多字节数据域的值从网络字节序转换为本地机器字节序,同样在消息构造时应从本地机器字节序转换为网络字节序。BinMessageParser的实现如下所示(请参考图2-3所示的格式定义):
HalfStructuredMessageSmartPtr BinMessageParser::Parse(const ServiceLayerMessage& originalMsg) { HalfStructuredMessageSmartPtr pResultObject( / new BinHalfStructuredMessage()); MessageHeaderSmartPtr pMessageHeader(new MessageHeader);
// 解析消息头 pMessageHeader->SetMessageType((MessageType)originalMsg[0]);
u_long source_in_netwk = 0; BYTE *pSource = (BYTE*)&source_in_netwk; pSource[0] = originalMsg[1]; // 取出source pSource[1] = originalMsg[2]; pSource[2] = originalMsg[3]; pSource[3] = originalMsg[4]; ClientID source = (ClientID)::ntohl(source_in_netwk); pMessageHeader->SetSource(source);
u_long dest_in_netwk = 0; BYTE *pDest = (BYTE*)&dest_in_netwk; pDest[0] = originalMsg[5]; // 取出dest pDest[1] = originalMsg[6]; pDest[2] = originalMsg[7]; pDest[3] = originalMsg[8]; ClientID dest = (ClientID)::ntohl(dest_in_netwk); pMessageHeader->SetDestination(dest);
u_short sn_in_netwk = 0; BYTE *pSn = (BYTE*)&sn_in_netwk; pSn[0] = originalMsg[9]; // 取出sn pSn[1] = originalMsg[10]; SerialNumber sn = (SerialNumber)::ntohs(sn_in_netwk); pMessageHeader->SetSn(sn);
u_long msgid_in_netwk; BYTE *pMsgId = (BYTE*)&msgid_in_netwk; pMsgId[0] = originalMsg[11]; // 取出msgid pMsgId[1] = originalMsg[12]; pMsgId[2] = originalMsg[13]; pMsgId[3] = originalMsg[14]; MessageID msgid = (MessageID)::ntohl(msgid_in_netwk); pMessageHeader->SetMessageID(msgid); // 完成消息头的解析 pResultObject->m_msgHeader = pMessageHeader; // 拷贝消息体 pResultObject->m_msgBody.assign( / originalMsg.begin()+15, originalMsg.end()); return pResultObject; } |
宏DECLARE_STATIC_SINGLETON()将所在类声明为一个单例类,其具体实现和使用方法请参考本章2.16节。
现在NetworkObserverImpl实现类的构造函数可以改写为:
NetworkObserverImpl::NetworkObserverImpl() { m_msgParser = MessageParserFactory::get_instance(); } |
这样就可以做到与具体业务消息格式无关。