文档章节

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

零下三度
 零下三度
发布于 2014/04/22 12:14
字数 1817
阅读 162
收藏 4

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

<BODY>
      <LOOP>
         <NAME>jerry</NAME>
         <AGE>21</AGE>
      </LOOP>
      <LOOP>
         <NAME>mark</NAME>
         <AGE>22</AGE>
      </LOOP>
</BODY>

       在上面的报文BODY部分,两个LOOP存在的情况,无法通过其他属性来标识两个LOOP的差异属性,在做缓存的时候,无法通过key去区分这两个LOOP的缓存信息。还有一种情况

<BODY>
   <LOOP_NUM>2<LOOP_NUM>
   <LOOP>
      <CUST_NAME>阿毛</CUST_NAME>
      <CUST_IDCARD_NO>11111111111111111</CUST_IDCARD_NO>
   </LOOP>
   <LOOP>
      <AGENT_NAME>阿狗</CUST_NAME>
      <AGENT_IDCARD_NO>2222222222222222</CUST_IDCARD_NO>
      <TRAN_TIME>20140411</TRAN_TIME>
   </LOOP>
</BODY>

      两个Loop里面的内容还不一致。这样的报文结构设计,哎……当我们去从别人的系统中获取数据或者提交数据,也会碰到,这样的报文结构如果存入数据库中,如何缓存到内存中呢?我没有找到比较好的方法。于是我就采用了最笨的方法是用StringBuilder(或者StringBuffer去拼接xml字符串),当然能够完成工作,但是每个交易的报文拼接要写几百行的代码,而且不易维护,如果别人来维护我的代码,我估计他要疯了。

    于是我开始寻找一些方法,去解决这些问题。后面我想到了一个方案,不知道好不好,但是能够解决我的问题,而且不用修改代码了。

    我的方案是每个交易的报文写成一个xml文件,采用类似EL表达式一样的法则去给报文赋值。如下所示:

<BODY>
   <LOOP_NUM>${LOOP_NUM}<LOOP_NUM>
   <LOOP>
      <CUST_NAME>${CUST_NAME}</CUST_NAME>
      <CUST_IDCARD_NO>${CUST_IDCARD_NO}</CUST_IDCARD_NO>
   </LOOP>
   <LOOP>
      <AGENT_NAME>${AGENT_NAME}</CUST_NAME>
      <AGENT_IDCARD_NO>${AGENT_IDCARD_NO}</CUST_IDCARD_NO>
      <TRAN_TIME>${TRAN_TIME}</TRAN_TIME>
   </LOOP>
</BODY>

   这样的好处有如下两点:

   1.修改接口报文不需要修改代码或者数据库了,直接改xml文件。

   2.接口报文赋值修改只需要将${name}修改为${name_1}.

   对于其他竞争公司经常修改报文故意刁难自己的时候,不要担心这个他们疯狂的接口变更了!!让他们自作孽去吧!

  闲话不多说了。开始设计。

 首先应该读取xml文件并缓存。

/***
 * 
 * @author 零下三度(huangqian866@163.com)
 *
 */
public class FileIOUtils {
	/***
	 * 默认类路径
	 */
	public static final String DEFAULT_CLASS_PATH = System
			.getProperty("user.dir") + File.separator + "src";

	public static final String CHARSET = "UTF-8";


	/***
	 * 文件过滤器,过滤出xml文件
	 */
	private static FileFilter filter = new FileFilter() {
		@Override
		public boolean accept(File pathname) {
			if (pathname.isFile()
					&& pathname.getName().toLowerCase().endsWith(".xml")) {
				return true;
			}
			return false;
		}
	};

	/***
	 * 获取指定目录下的所有xml文件的绝对路径
	 * 
	 * @param dirPath
	 *            目录路径
	 * @return
	 */
	public static List<String> getXmlFileAbspath(String dirPath) {

		ArrayList<String> fileNames = new ArrayList<String>();
		if (dirPath == null || dirPath.trim().isEmpty()) {
			dirPath = DEFAULT_CLASS_PATH;
		}
		File dir = new File(dirPath);
		if (dir.exists()) {// 文件存在
			if (dir.isDirectory()) {// 文件为目录路径
				File[] xmlFiles = dir.listFiles(filter);
				for (File file : xmlFiles) {
					fileNames.add(file.getAbsolutePath());
					System.out.println(file.getAbsolutePath());
				}
			}
		}
		return fileNames;
	}

	/***
	 * 根据文件路径,获取文件内容
	 * 
	 * @param filePath
	 * @return
	 */
	public static String getContent(String filePath) {
		String context = "";
		try {
			File file = new File(filePath);
			if (file.exists() && file.isFile()) {
				FileInputStream fis = new FileInputStream(file);
				byte buffer[] = new byte[fis.available()];
				fis.read(buffer, 0, fis.available());
				context = new String(buffer, CHARSET);
				fis.close();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return context;
	}

}

 缓存类代码如下:

/***
 * 
 * @author 零下三度(huangqian866@163.com)
 *
 */
public class FileIOUtils {
	/***
	 * 默认类路径
	 */
	public static final String DEFAULT_CLASS_PATH = System
			.getProperty("user.dir") + File.separator + "src";

	public static final String CHARSET = "UTF-8";


	/***
	 * 文件过滤器,过滤出xml文件
	 */
	private static FileFilter filter = new FileFilter() {
		@Override
		public boolean accept(File pathname) {
			if (pathname.isFile()
					&& pathname.getName().toLowerCase().endsWith(".xml")) {
				return true;
			}
			return false;
		}
	};

	/***
	 * 获取指定目录下的所有xml文件的绝对路径
	 * 
	 * @param dirPath
	 *            目录路径
	 * @return
	 */
	public static List<String> getXmlFileAbspath(String dirPath) {

		ArrayList<String> fileNames = new ArrayList<String>();
		if (dirPath == null || dirPath.trim().isEmpty()) {
			dirPath = DEFAULT_CLASS_PATH;
		}
		File dir = new File(dirPath);
		if (dir.exists()) {// 文件存在
			if (dir.isDirectory()) {// 文件为目录路径
				File[] xmlFiles = dir.listFiles(filter);
				for (File file : xmlFiles) {
					fileNames.add(file.getAbsolutePath());
					System.out.println(file.getAbsolutePath());
				}
			}
		}
		return fileNames;
	}

	/***
	 * 根据文件路径,获取文件内容
	 * 
	 * @param filePath
	 * @return
	 */
	public static String getContent(String filePath) {
		String context = "";
		try {
			File file = new File(filePath);
			if (file.exists() && file.isFile()) {
				FileInputStream fis = new FileInputStream(file);
				byte buffer[] = new byte[fis.available()];
				fis.read(buffer, 0, fis.available());
				context = new String(buffer, CHARSET);
				fis.close();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return context;
	}

}

      完成文件读取和内容缓存,接下来就是格式化替代的工作了。java中有一个自带的MessageFormat类,其麻烦支出在于参数必须按照顺序给,否则就乱了。接口开发的时候尽量要使用者方便一些,所以在这里采用正则表达式去替换。

     格式化类代码如下:

/***
 * 
 * @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,String> data,String srcXmlMessage) {
		 Pattern pattern = Pattern.compile(REGEX_PATTERN);
         Matcher matcher = pattern.matcher(srcXmlMessage);
         while(matcher.find()){
	          	String key = matcher.group(2);
	          	String val = data.get(key);
	          	val = val==null ? "" : val.trim();
	          	String replaceRegexPattern = "\\$\\{"+key+"\\}";
	          	srcXmlMessage = srcXmlMessage.replaceAll(replaceRegexPattern, val);
	          }
		return srcXmlMessage;
	}
	

}

在完成读取xml文件和字符串格式化类后,接下来的就是报文工厂了,代码如下:

/***
 * 
 * @author 零下三度(huangqian866@163.com)
 *
 */
public class MessageFactory {
	
	private static final Formatable format = new XmlMessageFormat();
	
	
	public static String creatXmlMessage(String tranNo,HashMap<String,String> 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");
		}
		return format.format(data, content);
	}

}

    好了。以上是基础的架子,写出来了,还没有完,必须单元测试一下。这里就不详细测试各个方法了,只写一个creatXmlMessage方法的测试用例。

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

	@Test
	public void test() {
	
		String tranNo = "1009";
		HashMap<String,String> data = new HashMap<String,String>();
		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");
		data.put("ZHUCZJ", "1111111111.11");
		data.put("HANGYL", "0");
		data.put("ZZZCDZ", "北京市东城区北京市东城区");
		data.put("SFRENMC", "阿毛");
		data.put("SUSDDM", "320100");
		data.put("FRENMC", "111");
		String xml = MessageFactory.creatXmlMessage(tranNo, data);
		System.out.println(xml);
	}

}

  报文xml文件1009.xml其中内容是:

<?xml version="1.0" encoding="GBK"?>
<Transaction>
	<BODY>
		<YTD_IP>${YTD_IP}</YTD_IP>
		<QUEUE_NO>${QUEUE_NO}</QUEUE_NO>
		<LOOPNUM>${LOOPNUM}</LOOPNUM>
		<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>
		<TYPE>Z2</TYPE>
	</BODY>
	<HEAD>
		<EXT_HEAD>
			<BUSINESS_ID>${BUSINESS_ID}</BUSINESS_ID>
			<MAC_INDEX></MAC_INDEX>
			<MAC_VALUE></MAC_VALUE>
		</EXT_HEAD>
		<TRAN_TERM></TRAN_TERM>
		<USER_ID>${USER_ID}</USER_ID>
		<CHANNEL_ID>${CHANNEL_ID}</CHANNEL_ID>
		<TRAN_TIME>${TRAN_TIME}</TRAN_TIME>
		<BANK_CODE>${BANK_CODE}</BANK_CODE>
		<SERVER_ID></SERVER_ID>
		<AUTH_ID></AUTH_ID>
		<AUTH_CONTEXT></AUTH_CONTEXT>
		<SERVICE_ID>${SERVICE_ID}</SERVICE_ID>
		<TRAN_DATE>${TRAN_DATE}</TRAN_DATE>
		<SEQ_NO>${SEQ_NO}</SEQ_NO>
	</HEAD>
</Transaction>

运行测试用例,测试结果如下:

<?xml version="1.0" encoding="GBK"?>

<Transaction>

<BODY>

<YTD_IP>66.12.18.20</YTD_IP>

<QUEUE_NO></QUEUE_NO>

<LOOPNUM>1</LOOPNUM>

<LOOP>

<CODERECORD>

<ZHUCZJ>1111111111.11</ZHUCZJ>

<HANGYL>0</HANGYL>

<ZZZCDZ>北京市东城区北京市东城区</ZZZCDZ>

<SFRENMC>阿毛</SFRENMC>

<SUSDDM>320100</SUSDDM>

<FRENMC>111</FRENMC>

<SJZGZH></SJZGZH>

</CODERECORD>

<TRANCODE></TRANCODE>

</LOOP>

<TYPE>Z2</TYPE>

</BODY>

<HEAD>

<EXT_HEAD>

<BUSINESS_ID>Y001</BUSINESS_ID>

<MAC_INDEX></MAC_INDEX>

<MAC_VALUE></MAC_VALUE>

</EXT_HEAD>

<TRAN_TERM></TRAN_TERM>

<USER_ID>001</USER_ID>

<CHANNEL_ID>04</CHANNEL_ID>

<TRAN_TIME>095104</TRAN_TIME>

<BANK_CODE>9901</BANK_CODE>

<SERVER_ID></SERVER_ID>

<AUTH_ID></AUTH_ID>

<AUTH_CONTEXT></AUTH_CONTEXT>

<SERVICE_ID>Y001</SERVICE_ID>

<TRAN_DATE>20140418</TRAN_DATE>

<SEQ_NO>2014022800010502</SEQ_NO>

</HEAD>

</Transaction>

完成了功能。

思考

   这里有一个问题。无法完成foreach的能。比如说,我要查询学生表的前20条记录的响应报文,这个功能现在还不能根据实际个数去循环组转报文,这个是后续努力的方向。如果这个实现了,那么组装报文就一切都很简单了。


© 著作权归作者所有

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

我的上一篇博客园【 采用加载XML文件的形式组转通讯报文,通过类似EL表示的方式赋值】能够解决绝大部分报文组装功能,但是有一种情况,它不能适用,就是当组装响应报文是,是查询某个表的前n...

零下三度
2014/04/22
0
0
java有没有不同报文格式、字段之间映射的工具或框架

我们知道各业务系统之间通讯,需要交换报文,而每个厂商开发的业务系统,定义的报文格式和字段命名不尽相同。如果有A、B两个系统之间要对接,最笨的方法就是,其中一个系统A基于另一个系统B...

hy86
2018/08/14
337
3
struts2,文件下载疑惑

问题描述: 我在使用struts2书写一个下载的功能,对于存在于文件服务器的文件,采用的是根据路径获得其inputstream流,进而输出到客户端,这里遇到一个疑问 Q:在这种情况下,文件的大小如何...

Anger_Coder
2013/10/22
219
5
bboss mvc接收和响应xml格式数据的方法

本文介绍bboss mvc接收和响应xml格式数据的方法 1.首先需要在bboss-mvc.xml文件中配置bboss mvc处理xml报文的插件XMLHttpMessageConverte:...

bboss
2013/12/28
0
0
基于XMPP协议的Android即时通信

设计基于开源的XMPP即时通信协议,采用C/S体系结构,通过GPRS无线网络用TCP协议连接到服务器,以架设开源的Openfn'e服务器作为即时通讯平台。 系统主要由以下部分组成:一是服务器,负责管理...

刘遇安
2014/08/28
96
0

没有更多内容

加载失败,请刷新页面

加载更多

搭建tftp服务

前言: 最近整理一些以前的学习笔记。 过去都是存储在本地,此次传到网络留待备用。 TFTP服务: TFTP:Trivial File Transfer Protocol 不重要文件传输协议 主要用来传送小文件,不支持认证和...

迷失De挣扎
今天
3
0
rest 的理解

rest:表现层状态转移。 什么是restful协议?https://en.wikipedia.org/wiki/Representational_state_transfer 使用restful的好处。 Rest是一种体系结构样式,他定义了一组用于创建web服务的...

xiaoxiao_go
今天
5
0
聊聊spring cloud的CachingSpringLoadBalancerFactory

序 本文主要研究一下spring cloud的CachingSpringLoadBalancerFactory CachingSpringLoadBalancerFactory spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/o......

go4it
昨天
4
0
一篇文章搞定——JDK8中新增的StampedLock

一、StampedLock类简介 StampedLock类,在JDK1.8时引入,是对读写锁ReentrantReadWriteLock的增强,该类提供了一些功能,优化了读锁、写锁的访问,同时使读写锁之间可以互相转换,更细粒度控...

须臾之余
昨天
5
0
Android Camera原理之CameraDeviceCallbacks回调模块

在讲解《Android Camera原理之openCamera模块(二)》一文的时候提到了CameraDeviceCallbacks回调,当时没有详细展开,本文我们详细展开讲解一下。 CameraDeviceCallbacks生成过程: 《Android...

天王盖地虎626
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部