個人上一篇博客園【 採用加載XML文件的形式組轉通信報文,經過相似EL表示的方式賦值】可以解決絕大部分報文組裝功能,可是有一種狀況,它不能適用,就是當組裝響應報文是,是查詢某個表的前n條記錄是,它不能自動控制循環體的個數。好比銀行轉行匯款中,查詢歷史交易信息(收款人姓名、地址、帳號、聯繫點)的前5條時,我上一篇博客的方案是不可以解決這樣的接口報文采用模板模式。java
那麼,對於報文Array、List的報文,能不能採用模板模式呢?正則表達式
答案必須是YES!緩存
簡單介紹一下個人方案。我定義了一個<FOR-EACH KEY="hist"> ..(循環重複的內容)..</FOR-EACH>標籤,標籤以內的部分是循環重複的內容。簡單的舉個例子模板以下:app
<BODY> <LIST> <FOR-EACH KEY="hist"> <STRUCT> <NAME>${name}</NAME> <AGE>${age}</AGE> </STRUCT> </FOR-EACH> <LIST> </BODY>
咱們期待可以產生的報文像下面同樣。ide
<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表達式的地方所有被空白替換了。單元測試
接下來就開始去實現了。測試
首先,文件讀取和緩存部分和此前的沒有變化,仍然採用原來的方案便可。
ui
其次,考慮一下XmlMessageFormat類的Foramt方法,其中數據部分再也不是Map<String,String>了。由於已經包含了List結構了,所以,修改爲以下所示:spa
/*** * * @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了,到如今,基本上支持絕大部分的報文的組裝工做了。
由於本身在公司負責接口這一塊,遇到的問題多了,就產生那麼點點想法,出差在外,看完球心血來潮,噼噼啪啪就寫下來了,留給之後的本身,也但願可以對到遇到相似問題的朋友們有一點點幫助。
聲明一下:大量的正則替換是至關影響性能的,實際應用有待商榷,目前正在尋找其餘方式替換「正則替換」的工做。