XML这种格式的字符串一般作为服务器之间的数据交互格式,且应用广泛,是一个已经很成熟的技术。先介绍一下XML是怎么生成的(肯定不是拼字符串拼出来的)。
现在给一个服务器应答的XML常用格式,应答XML报文一般比请求XML报文简单很多:
<RETURNS>
<RETCODE>0</RETCODE>
<RETMSG>SUCCESS</RETMSG>
</RETURNS>
这是A服务器向B服务器提交一个请求后,B进行相关的数据处理后并返回给A的处理结果,也是一个XML报文,一般上面这种结果表示B处理成功了。我们怎么生成一个这样的报文呢?下面上代码:
//创建xml文档
XmlDocument doc = new XmlDocument();
//创建节点,节点名称为RETURNS
XmlElement Root = doc.CreateElement("RETURNS");
//将这个节点加入文档中
doc.AppendChild(Root);
//创建一个名称为RETCODE的节点
XmlElement child1 = doc.CreateElement("RETCODE");
//给该节点赋值0
child1.InnerText = "0";
//将该节点加入到RETURNS节点下,RETMSG同理
Root.AppendChild(child1);
XmlElement child2 = doc.CreateElement("RETMSG");
child2.InnerText = "SUCCESS";
Root.AppendChild(child2);
//将设计好的XML作为字符串输出
Console.Write(doc.InnerXml);
Console.Read();
看看生成的结果:
当A服务器拿到这样的XML后同样需要进行解析。这里需要搞清楚的是,对A服务器来说,它关心的是什么?它只关心B服务器的返回结果怎么样,成功还是失败。如果成功,那么A可能会做后续的事情,如果失败,B会给出失败原因。但B响应完成后,A服务器后面要做什么就跟B服务器没有任何关系了,而且不论是否成功,这个结果一般都会保存到数据库,作为以后的责任认定的依据(虽然DBA可以直接修改,但处理结果一般是双方服务器都会保存的,所以只要双方服务器处理结果不一致基本确定是数据库直接修改的,这个是职业素养问题了,不过多纠结)。
对于返回值RETCODE一般都约定0为处理成功。当然,你想奇葩点把1当做处理成功都是可以的,反正双方技术人员约定好就行了。接下来看一下C#怎么解析XML:
//需要注意,这个xml字符串一定要有声明头,且节点内不允许有空格,如<RETURNS >这样是不允许的
string response = "<?xml version=\"1.0\" encoding=\"utf - 8\" ?>
<RETURNS>
<RETCODE>0</RETCODE>
<RETMSG>SUCCESS</RETMSG>
</RETURNS>";
XmlDocument resDoc = new XmlDocument();
resDoc.InnerXml = response;
//得到这个xml根节点下所有子节点,比如这里是有2个,第一个为声明头,第二个是RETURNS,我们需要拿到第二个节点
XmlNodeList xnl = resDoc.ChildNodes;
Console.WriteLine("节点数量:"+xnl.Count);
Console.WriteLine("第一个节点名称为:" + xnl.Item(0).Name);
Console.WriteLine("第二个节点名称为:" + xnl.Item(1).Name);
Console.ReadLine();
//解析节点内容,声明头不用管它,从第二个节点开始解析
XmlNode xn=xnl.Item(1);
XmlNodeList returns=xn.ChildNodes;
XmlNode retCode = returns.Item(0);
Console.WriteLine("{0}节点的内容为:{1}", retCode.Name, retCode.InnerText);
XmlNode retMsg = returns.Item(1);
Console.WriteLine("{0}节点的内容为:{1}", retMsg.Name, retMsg.InnerText);
Console.ReadLine();
一般的情况下,作为双方服务器数据交互格,格式是不会轻易改变的,如果需要更改格式,双方技术人员肯定会做交流,更改解析格式,所以放心解析就好。而且数据是经过加密传输的,形式上肯定不会这么简单,但核心的思想都是这样。然后解析的结果最终一定会保存到数据库,这里就不再做数据存储了。
现在有一个这样的业务场景,比如客户到淘宝上买了一样东西,OMS对应的商户接收到客户数据,该商户的实物实际在WMS进行存储,并且已经与指定的WMS对接完毕,那么该数据组织好后会由OMS平台向WMS发起请求,请求XML如下:
<?xml version="1.0" encoding="utf - 8" ?>
<REQUEST>
<ORDERS>
<ORDER>
<ORDERHEAD> //订单头部信息
<ORDERID>PTB201903291049233</ORDERID> //OMS订单号
<STORERID>LDT</STORERID> //商户商标
<ADDDATE>2019-03-29 10:51:00</ADDDATE> //订单生成时间
<CONSIGNEEID>路人甲</CONSIGNEEID> //客户姓名
<MOBILE>13333333333</MOBILE> //客户电话
<ADDRESS>天国1路</ADDRESS> //客户地址
</ORDERHEAD>
<ORDERDETAIL> //订单详细信息
<ORDER>
<ORDERID>PTB201903291049233</ORDERID> //对应订单的编号
<ITEMID>GB2312</ITEMID> //货品编号
<ITEMNAME>振动棒</ITEMNAME> //货品名称
<COUNT>2</COUNT> //购买的数量
</ORDER>
<ORDER>
<ORDERID>PTB201903291049233</ORDERID> //对应订单的编号
<ITEMID>UTF8</ITEMID> //货品编号
<ITEMNAME>双头龙</ITEMNAME> //货品名称
<COUNT>1</COUNT> //购买的数量
</ORDER>
</ORDERDETAIL>
</ORDER>
</ORDERS>
</REQUEST>
这段XML就相对复杂一些,但解析思想还是这样。事实上,A服务器发起的这一段XML请求一定是经过加密的,B服务器得到的是一串密文。在服务器对接的时候,先有一个用户授权,双方做服务器交互会用一个约定的秘钥进行加解密,主要用于XML防篡改。简单的说就是,一个真正的业务绝对不会有这么简单的XML。我们尝试解析这段XML
string requestXML="<?xml version=\"1.0\" encoding=\"utf - 8\" ?><REQUEST><ORDERS><ORDER><ORDERHEAD><ORDERID>PTB201903291049233</ORDERID><STORERID>LDT</STORERID><ADDDATE>2019-03-29 10:51:00</ADDDATE><CONSIGNEEID>路人甲</CONSIGNEEID><MOBILE>13333333333</MOBILE><ADDRESS>天国1路</ADDRESS></ORDERHEAD><ORDERDETAIL><ORDER><ORDERID>PTB201903291049233</ORDERID><ITEMID>GB2312</ITEMID><ITEMNAME>振动棒</ITEMNAME><COUNT>2</COUNT></ORDER><ORDER><ORDERID>PTB201903291049233</ORDERID><ITEMID>UTF8</ITEMID><ITEMNAME>双头龙</ITEMNAME><COUNT>1</COUNT></ORDER></ORDERDETAIL></ORDER></ORDERS></REQUEST>";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.InnerXml = requestXML;
XmlNodeList xmlNodeList=xmlDocument.ChildNodes;
//拿到ORDERS节点,这里才是数据真正存在的节点,一个XML实际上不止传一个订单,肯定需要支持多订单解析,读者根据实际XML自行判断该从哪里开始解析
XmlNode requestNode = xmlNodeList.Item(1);
XmlNode ordersNode=requestNode.ChildNodes.Item(0); //这里拿到ORDERS节点
Console.WriteLine("开始解析{0}节点",ordersNode.Name);
XmlNodeList orderList = ordersNode.ChildNodes; //获取ORDERS节点下的所有节点
for(int i=0;i<orderList.Count;i++)
{
XmlNode order = orderList.Item(i);
Console.WriteLine("解析ORDERHD");
XmlNode orderHeadNode = order.ChildNodes.Item(0);
XmlNodeList orderHeadNodeNext = orderHeadNode.ChildNodes;
for (int j=0;j<orderHeadNodeNext.Count;j++)
{
Console.WriteLine(orderHeadNodeNext.Item(j).Name + ":" + orderHeadNodeNext.Item(j).InnerText);
}
Console.WriteLine();
Console.WriteLine("解析ORDERDETAIL");
XmlNode orderDetailNode = order.ChildNodes.Item(1);
XmlNodeList orderDetailNodeNext = orderDetailNode.ChildNodes;
for(int m=0;m<orderDetailNodeNext.Count;m++)
{
XmlNode detailNode = orderDetailNodeNext.Item(m);
XmlNodeList detailOrderList = detailNode.ChildNodes;
for(int n=0;n<detailOrderList.Count;n++)
{
Console.WriteLine(detailOrderList.Item(n).Name+":"+detailOrderList.Item(n).InnerText);
}
Console.WriteLine();
}
}
Console.ReadLine();
如果放在C#里面解析相对来说还是很麻烦的,解析出来之后还需要用一个结构和xml一致的类去保存这些数据,最终会用一个List容器来承载这些数据,然后再保存到数据库中。其实数据库自身也是支持xml解析的,而且逻辑上比使用C#会简单很多,业务处理也方便很多。看读者跟擅长哪块吧。
这里继续,解析出来了肯定不能直接保存的,基本的数据校验还是要的吧,而且给定的xml不是所有的数据都是需要的,而且有些关键性的数据一定要做相关校验,比如订单号和商户编号,我WMS没有接入这个商户编号也传给我?这就是业务校验。读者依据实际业务做相关更改,数据处理完毕再生成响应报文,告诉对方我的处理结果即可。
有时候请求报文不一定是XML,要求使用JSON进行数据交互也是有可能的,这时候就需要进行格式转换再发起请求,有开发者早已封装好相关的功能,我们只需要调用即可完成格式的转换,工具类名称叫Newtonsoft.Json.dll,该资源已上传,有需要的读者自行下载。将下载的工具类放入自己的项目文件夹中,添加该dll引用即可使用。
下面给出使用方法
string requestXML = "<?xml version=\"1.0\" encoding=\"utf - 8\" ?><REQUEST><ORDERS><ORDER><ORDERHEAD><ORDERID>PTB201903291049233</ORDERID><STORERID>LDT</STORERID><ADDDATE>2019-03-29 10:51:00</ADDDATE><CONSIGNEEID>路人甲</CONSIGNEEID><MOBILE>13333333333</MOBILE><ADDRESS>天国1路</ADDRESS></ORDERHEAD><ORDERDETAIL><ORDER><ORDERID>PTB201903291049233</ORDERID><ITEMID>GB2312</ITEMID><ITEMNAME>振动棒</ITEMNAME><COUNT>2</COUNT></ORDER><ORDER><ORDERID>PTB201903291049233</ORDERID><ITEMID>UTF8</ITEMID><ITEMNAME>双头龙</ITEMNAME><COUNT>1</COUNT></ORDER></ORDERDETAIL></ORDER></ORDERS></REQUEST>";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.InnerXml = requestXML;
/*
* 如果直接传入XmlDocument,最后生成的Json字符串会有Xml声明头,
* 一般生成的Json字符串应该没有xml声明头的,所以传入的XmlNode要调整一下
*/
string jsonString = JsonConvert.SerializeXmlNode(xmlDocument.ChildNodes.Item(1));
Console.WriteLine(jsonString);
Console.Read();