採用加載XML文件的形式組轉通信報文,經過相似EL表示的方式賦值

    雖然有Json,可是接口報文大部分時候採用的XML。接口開發中最麻煩的是接口報文不斷變化,使用數據庫記錄報文結構當然能夠應對接口的變化,可是有些時候緩存沒法設計,好比以下這個報文的BODY部分:java

<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表達式同樣的法則去給報文賦值。以下所示:ide

<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>

   這樣的好處有以下兩點:oop

   1.修改接口報文不須要修改代碼或者數據庫了,直接改xml文件。單元測試

   2.接口報文賦值修改只須要將${name}修改成${name_1}.測試

   對於其餘競爭公司常常修改報文故意刁難本身的時候,不要擔憂這個他們瘋狂的接口變動了!!讓他們自做孽去吧!ui

  閒話很少說了。開始設計。spa

 首先應該讀取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條記錄的響應報文,這個功能如今還不能根據實際個數去循環組轉報文,這個是後續努力的方向。若是這個實現了,那麼組裝報文就一切都很簡單了。

相關文章
相關標籤/搜索