文档章节

  再谈采用加载XML文件的形式组转通讯报文,通过类似EL表示的方式赋值,补充foreach

零下三度
 零下三度
发布于 2014/04/22 23:49
字数 1668
阅读 137
收藏 4

      我的上一篇博客园【 采用加载XML文件的形式组转通讯报文,通过类似EL表示的方式赋值】能够解决绝大部分报文组装功能,但是有一种情况,它不能适用,就是当组装响应报文是,是查询某个表的前n条记录是,它不能自动控制循环体的个数。比如银行转行汇款中,查询历史交易信息(收款人姓名、地址、账号、联系点)的前5条时,我上一篇博客的方案是不能够解决这样的接口报文采用模板模式。

       那么,对于报文Array、List的报文,能不能采用模板模式呢?

      答案必须是YES!

      简单介绍一下我的方案。我定义了一个<FOR-EACH KEY="hist"> ..(循环重复的内容)..</FOR-EACH>标签,标签之内的部分是循环重复的内容。简单的举个例子模板如下:

<BODY>
    <LIST>
    <FOR-EACH KEY="hist">
      <STRUCT>
       <NAME>${name}</NAME>
       <AGE>${age}</AGE>
       </STRUCT>
    </FOR-EACH>
    <LIST>
</BODY>

我们期待能够产生的报文像下面一样。

<BODY>
    <LIST>
       <STRUCT>
       <NAME>卡卡</NAME>
       <AGE>30</AGE>
       </STRUCT>
       <STRUCT>
       <NAME>C罗</NAME>
       <AGE>28</AGE>
       </STRUCT>
    <LIST>
</BODY>

     接下来详细介绍一下我的设计方案。在这里我采用正则表达式去捕获FOR-EACH标签、KEY的值、循环部分的内容。KEY是在报文工厂的数据Map结构中,循环数据List的key。循环部分可以采用上篇博客的XmlMessageFormat的format方法格式循环部分的字符串,然后用格式化的字符串替换原来处理FOR-EACH包含的内容。

   注意:这里应该先替换foreach部分的内容,否则foreach部分的类似EL表达式的地方全部被空白替换了

  接下来就开始去实现了。

 首先,文件读取和缓存部分和此前的没有变化,仍然采用原来的方案即可。

  其次,考虑一下XmlMessageFormat类的Foramt方法,其中数据部分不再是Map<String,String>了。因为已经包含了List结构了,因此,修改成如下所示:

/***
 * 
 * @author huangqian(huangqian866@163.com)
 *
 */
public class XmlMessageFormat implements Formatable {
	
	/***
	 * XML中值替换的正则表达式模式,例如${name}
	 */
	public static final String REGEX_PATTERN = "(\\$\\{)(\\w+)(\\})";
	
	

	@Override
	public String format(Map<String,Object> data,String srcXmlMessage) {
		 Pattern pattern = Pattern.compile(REGEX_PATTERN);
         Matcher matcher = pattern.matcher(srcXmlMessage);
         while(matcher.find()){
	          	String key = matcher.group(2);
	          	String val = (String)data.get(key);
	          	val = val==null ? "" : val.trim();
	          	String replaceRegexPattern = "\\$\\{"+key+"\\}";
	          	srcXmlMessage = srcXmlMessage.replaceAll(replaceRegexPattern, val);
	          }
		return srcXmlMessage;
	}
	
	
	

}

      接下来,考虑ForeachFormat的设计了。在这里关键点是FOR-EACH标签的正则表达式的设计了,好好分析一下,我需要FOR-EACH标签包含的全部内容、KEY的值、还有FOR-EACH孩子部分是循环的部分,此外还要考虑空格的问题,这样我就可以得出正则表达式:(<FOR-EACH[\\s]+KEY=\")(\\w+)(\"\\s*>)([\\s\\S]+)(</FOR-EACH>)

    接下来就是去实现了,废话不多说,直接上代码:

/***
 * 
 * @author 零下三度(huangqian866@163.com)
 * 
 */
public class ForeachFormat implements Formatable {

	/***
	 * FOR-EACH标签的捕获正则表达式
	 */
	private static final String FOREACH_REGEX_PATTERN = "(<FOR-EACH[\\s]+KEY=\")(\\w+)(\"\\s*>)([\\s\\S]+)(</FOR-EACH>)";


	/***
	 * XML中<FOR-EACH></FOR-EACH>的格式化
	 */
	@Override
	public String format(Map<String, Object> data, String srcXmlMessage) {
		Pattern pattern = Pattern.compile(FOREACH_REGEX_PATTERN);
		Matcher matcher = pattern.matcher(srcXmlMessage);
		String newXmlPart;
		while (matcher.find()) {
			String oldXml = matcher.group();
			System.out.println("oldXml:"+oldXml);
			String key = matcher.group(2);
			System.out.println("key:"+key);
			String foreachContent = matcher.group(4);
			System.out.println("foreachContent:" + foreachContent);
			List<Map<String,Object>> list = (List<Map<String,Object>>)data.get(key);
			newXmlPart = foreachFormat(list,foreachContent);
			srcXmlMessage = srcXmlMessage.replace(oldXml, newXmlPart);
		}
		return srcXmlMessage;
	}

	/***
	 * 格式化foreach中的内容
	 * 
	 * @param list
	 * @param foreachXml
	 * @return
	 */
	private  String foreachFormat(List<Map<String, Object>> list,
			String foreachXml) {
		StringBuilder xml = new StringBuilder();
		XmlMessageFormat xmlFormat = new XmlMessageFormat();
		String formatRstStr;
		if(list != null){
			for (Map<String, Object> item : list) {
				formatRstStr = xmlFormat.format(item, foreachXml);
				xml.append(formatRstStr);
			}
		}
		return xml.toString();
	}

	public static void main(String[] args) {
		String xml = "<?xml version=\"1.0\" encoding=\"GBK\"?>"
				+ "<Transaction>" + "<BODY>" + "<YTD_IP>${YTD_IP}</YTD_IP>"
				+ "<FOR-EACH KEY=\"foreach\">" + "<LOOP>" + "<CODERECORD>"
				+ "<ZHUCZJ>${ZHUCZJ}</ZHUCZJ>" + "<HANGYL>${HANGYL}</HANGYL>"
				+ "<ZZZCDZ>${ZZZCDZ}</ZZZCDZ>"
				+ "<SFRENMC>${SFRENMC}</SFRENMC>"
				+ "<SUSDDM>${SUSDDM}</SUSDDM>" + "<FRENMC>${FRENMC}</FRENMC>"
				+ "<SJZGZH>${SJZGZH}</SJZGZH>" + "</CODERECORD>"
				+ "<TRANCODE></TRANCODE>" + "</LOOP>" + "</FOR-EACH>"
				+ "</BODY>" + "</Transaction>";
		
		ForeachFormat format = new ForeachFormat();
		format.format(null, xml);

	}

}

 现在功能都有了,调用顺序很重要,先要去替换FOR-EACH部分,否则FOR-EACH部分的类似EL表达式的地方全部被空格替换了,看看MessageFactory的代码吧:

/***
 * 
 * @author 零下三度(huangqian866@163.com)
 *
 */
public class MessageFactory {
	
	private static final Formatable xmlMessageFormat = new XmlMessageFormat();
	
	private static final Formatable foreachFormat = new ForeachFormat();
	
	
	public static String creatXmlMessage(String tranNo,HashMap<String,Object> data) throws NoSuchTranException {
		if(tranNo == null || tranNo.trim().isEmpty()){//交易号为null || isEmpty
			throw new NoSuchTranException("tranNo is Empty or null");
		}
		String content = FileCache.getInstance().getData(tranNo.trim());
		if(content == null){//未能在缓存中找到以tranNo为key的数据
			throw new NoSuchTranException("not found file content in FileCache");
		}
		//处理foreach
		content = foreachFormat.format(data, content);
		return xmlMessageFormat.format(data, content);
	}

}

好了,都写玩了,上一个creatXmlMessage方法的单元测试

@Test
	public void test2() {
	
		String tranNo = "1008";
		HashMap<String,Object> data = new HashMap<String,Object>();
		data.put("SEQ_NO", "2014022800010502");
		data.put("TRAN_DATE", "20140418");
		data.put("SERVICE_ID", "Y001");
		data.put("BANK_CODE", "9901");
		data.put("TRAN_TIME", "095104");
		data.put("CHANNEL_ID", "04");
		data.put("USER_ID", "001");
		data.put("BUSINESS_ID", "Y001");
		data.put("TYPE", "Z2");
		data.put("YTD_IP", "66.12.18.20");
		data.put("LOOPNUM", "1");
		HashMap<String,Object> item = new HashMap<String,Object>();
		List<HashMap> list = new ArrayList<HashMap>();
		item.put("ZHUCZJ", "123");
		item.put("HANGYL", "农业");
		item.put("ZZZCDZ", "北京市东城区北京市东城区");
		item.put("SFRENMC", "阿毛");
		item.put("SUSDDM", "320100");
		item.put("FRENMC", "111");
		list.add(item);
		HashMap<String,Object> item1 = new HashMap<String,Object>();
		item1.put("ZHUCZJ", "456");
		item1.put("HANGYL", "采矿");
		item1.put("ZZZCDZ", "上海");
		item1.put("SFRENMC", "阿狗");
		item1.put("SUSDDM", "320100");
		item1.put("FRENMC", "111");
		list.add(item1);
		data.put("STUDET", list);
		String xml = MessageFactory.creatXmlMessage(tranNo, data);
		System.out.println(xml);
	}

接下来是报文的模板1008.xml

<?xml version="1.0" encoding="GBK"?>
<Transaction>
	<BODY>
		<YTD_IP>${YTD_IP}</YTD_IP>
		<FOR-EACH KEY="STUDET">
		<LOOP>
			<CODERECORD >
				<ZHUCZJ>${ZHUCZJ}</ZHUCZJ>
				<HANGYL>${HANGYL}</HANGYL>
				<ZZZCDZ>${ZZZCDZ}</ZZZCDZ>
				<SFRENMC>${SFRENMC}</SFRENMC>
				<SUSDDM>${SUSDDM}</SUSDDM>
				<FRENMC>${FRENMC}</FRENMC>
				<SJZGZH>${SJZGZH}</SJZGZH>
			</CODERECORD>
			<TRANCODE></TRANCODE>
		</LOOP>
		</FOR-EACH>
	</BODY>
</Transaction>

运行测试用例得出结果如下(省略中其他地方的输出):

<?xml version="1.0" encoding="GBK"?>
<Transaction>
	<BODY>
		<YTD_IP>66.12.18.20</YTD_IP>
		
		<LOOP>
			<CODERECORD >
				<ZHUCZJ>123</ZHUCZJ>
				<HANGYL>农业</HANGYL>
				<ZZZCDZ>北京市东城区北京市东城区</ZZZCDZ>
				<SFRENMC>阿毛</SFRENMC>
				<SUSDDM>320100</SUSDDM>
				<FRENMC>111</FRENMC>
				<SJZGZH></SJZGZH>
			</CODERECORD>
			<TRANCODE></TRANCODE>
		</LOOP>
		
		<LOOP>
			<CODERECORD >
				<ZHUCZJ>456</ZHUCZJ>
				<HANGYL>采矿</HANGYL>
				<ZZZCDZ>上海</ZZZCDZ>
				<SFRENMC>阿狗</SFRENMC>
				<SUSDDM>320100</SUSDDM>
				<FRENMC>111</FRENMC>
				<SJZGZH></SJZGZH>
			</CODERECORD>
			<TRANCODE></TRANCODE>
		</LOOP>
		
	</BODY>
</Transaction>

    OK了,到现在,基本上支持绝大部分的报文的组装工作了。

     因为自己在公司负责接口这一块,遇到的问题多了,就产生那么点点想法,出差在外,看完球心血来潮,噼噼啪啪就写下来了,留给以后的自己,也希望能够对到遇到类似问题的朋友们有一点点帮助。

     声明一下:大量的正则替换是相当影响性能的,实际应用有待商榷,目前正在寻找其他方式替换“正则替换”的工作。

© 著作权归作者所有

零下三度
粉丝 8
博文 11
码字总数 13153
作品 0
朝阳
程序员
私信 提问
采用加载XML文件的形式组转通讯报文,通过类似EL表示的方式赋值

虽然有Json,但是接口报文大部分时候采用的XML。接口开发中最麻烦的是接口报文不断变化,使用数据库记录报文结构固然可以应对接口的变化,但是有些时候缓存无法设计,比如如下这个报文的BOD...

零下三度
2014/04/22
0
0
jQuery对象入门级介绍

本文由伯乐在线 -kmokidd 翻译。未经许可,禁止转载! 英文出处:smashingmagazine。欢迎加入翻译组。 你是否曾经见过像 这样的JavaScrip代码?或许你还会思考下 是什么,如果看到这些你都觉...

伯乐在线
2014/06/24
0
0
详解 Linux 下的用户管理、用户组管理和权限管理

最近和几个朋友开发项目,期间使用了一台服务器跑模型,这台服务器是多人公用的,很多人都在上面有自己的账号,互不干涉内政,一切看起来十分井然有序。近期,这个服务器上刚挂载了一块新硬盘...

崔庆才
2018/09/04
0
0
每天一个 Linux 命令(58): telnet 命令

原文出处:peida telnet命令通常用来远程登录。telnet程序是基于TELNET协议的远程登录客户端程序。Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户...

peida
2017/02/05
0
0
使用 Apache MINA 开发高性能网络应用程序 

本文将通过一个简单的问候程序 HelloServer 来介绍 MINA 的基础架构的同时演示如何使用 MINA 开发网络应用程序。 Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Ap...

红薯
2008/10/05
2.2K
1

没有更多内容

加载失败,请刷新页面

加载更多

数据科学热潮下的冷思考:什么才是最需要的技能?

全文共3087字,预计学习时长6分钟 图片来源:pexels.com/@freestocks 数据科学已经进入了稳定生产的成熟期,数据科学家所需的技能也在与时俱进。不仅是追求更高效的机器学习模型,在当下,推...

读芯术
15分钟前
0
0
48.Nginx访问日志 日志切割 静态文件不记录

12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件不记录日志和过期时间 12.10 Nginx访问日志: ~1.日志格式 vim /usr/local/nginx/conf/nginx.conf //搜索log_format log_format com...

oschina130111
19分钟前
1
0
好程序员分享Css详解bem书写规范

  好程序员分享Css详解bem书写规范,bem是基于组件的web开发方法。其思想是将用户界面分隔为独立的块,从而使开发复杂的UI界面变得更简单和快,且不需要粘贴复制便可复用现有代码。BEM由B...

好程序员IT
24分钟前
0
0
基于cm+cdh搭建大数据集群

第一部分:搭建基本环境 1、网络配置 vim /etc/sysconfig/network-scripts/ifcfg-ens32 service network restart vim /etc/hosts 192.168.15.121 node1 192.168.15.122 node2 192.168.15.123......

一个点一个点
32分钟前
0
0
[学]ngin反向代理搭建与配置

Nginx安装地址:https://www.cnblogs.com/wyd168/p/6636529.html (linux) 必须安装的4个包: nginx-1.1.10.tar.gz openssl-1.0.1t.tar.gz pcre-8.39.tar.gz zlib-1.2.11.tar.gz ng配置主要......

覃光林
38分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部