解析XML和JSON內容的一點技巧的實例程式碼分享

黄舟
發布: 2017-03-17 17:08:11
原創
1454 人瀏覽過

解析XMLJSON內容的一點技巧

概述

在沒有統一標準的情況下,一個系統對接多個外部系統往往會遇到請求介面回應資料異質的情況,有可能回傳的是XML,也有可能回傳
JSON。除了回傳類型不同,內容結構也不盡相同。以XML類型為例,
介面1返回內容

<root>
    <bizKey>16112638767472747178067</bizKey>
    <returnMsg>OK</returnMsg>
    <returnCode>200</returnCode>
    ...
</root>
登入後複製

介面2返回內容

<root>
    <bid>16112638767472747178068</bid>
    <note>成功</note>
    <returnStatus>1</returnStatus>
    ...
</root>
登入後複製

如果在我們系統中為每種格式的內容針對處理顯然是不合理的,上面的內容中我們只是關心三種訊息,分別是業務ID、狀態值和描述訊息,那麼可不可以抽象化這三種訊息,在
取得這些訊息後再進行業務邏輯處理。

解析XML和JSON

根據業務抽象化我們需要從XML或JSON內容中獲得三種信息,我們這裡將會使用XPath和JSONPath的方式來解析。例如取得介面1的重要訊息,
我們可以設定三個XPath表達式,

{
    bid: "/root/bizKey",
    code: "/root/returnCode",
    description: "/root/returnMsg"
}
登入後複製

#bid,codedescription對應我們系統自己定義的字段名。
解析JSON內容也是同理的,只不過定義的是JSONPath表達式。

分兩步驟處理資料內容

假設我們從原始的XML和JSON資料中獲得了bid,code#和 description訊息,
從介面1獲得

{
    bid: &#39;16112638767472747178067&#39;,
    code: &#39;200&#39;,
    description: &#39;OK&#39;
}
登入後複製

從介面2獲得

{
    bid: &#39;16112638767472747178068&#39;,
    code: &#39;1&#39;,
    description: &#39;成功&#39;
}
登入後複製

假設我們從介面1文件獲知狀態值200表示請求成功,從介面2文檔獲知狀態值1表示請求成功,雖然他們都表示請求成功,但是我們還是不能
把他們原原本本地保存到我們的業務相關表中(當然這些響應數據還是需要保存到另外的記錄表中的,至少方便排查問題)。
假設我們的業務相關表格是這樣設計的

欄位名稱類型描述
#bidstring業務ID
codeint狀態值,0=初始,1=請求中,2=成功,3=失敗
#descriptionstring描述

因此,我们还必须定义规则把接口1返回的状态值200转换为我们系统的2,把接口2返回的状态值1转换为我们系统的2
总结一下,两步走解析XML和JSON数据内容

  1. 根据XPath或者JSONPath表达式解析获得重要信息

  2. 根据规则转换状态值

第一步解析数据获得重要信息

以XML为例,

public class XmlParseUtils {
    private DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    private XPathFactory xpathFactory = XPathFactory.newInstance();
    
    /**
     * 
     * @param param    数据内容
     * @param paths 表达式
     * @return
     * @throws Exception
     */
    public Map<String,Object> parse(String param, Map<String,String> paths) throws Exception{
        InputSource inputSource = new InputSource(new StringReader(param));
        Document document = dbFactory.newDocumentBuilder().parse(inputSource);
        Map<String,Object> map = Maps.newHashMap();
        for(String key : paths.keySet()) {
            XPath xpath = xpathFactory.newXPath();
            Node node = (Node) xpath.evaluate(paths.get(key), document, XPathConstants.NODE);
            if(node == null) {
                throw new Exception("node not found, xpath is " + paths.get(key));
            }
            map.put(key, node.getTextContent());
        }
        return map;
    }

}
登入後複製

parse函数的返回类型也可以是Map<String,String>,暂且用Map<String,Object>

第二步根据规则转换状态值

这一步稍稍有点麻烦,不过我们先不考虑代码实现,反正你能想到的可能别人已经帮你实现了。首先我们根据接口文档定义规则,写出规则表达式(或者其他的什么),
又是表达式。假设接口1的返回的状态值比较简单,只有200表示成功,其他情况都是失败,那么我们可以这样定义规则,

code.equals(&quot;200&quot;) ? 2: 3
登入後複製

或者

<#if code == "200">
2
<#else>
3
<#/if>
登入後複製
登入後複製

亦或者

function handle(arg) {
    if(arg == 200) {
        return 2;
    }
    return 3;
}
handle(${code})
登入後複製

以上根据同一份文档定义了三种不同类型的状态值转换规则,肯定需要三种不同的实现。下面一一说明,

三目表达式

code.equals(&quot;200&quot;) ? 2: 3是一个三目表达式,我们将使用jexl引擎来解析,利用第一步解析数据获得重要信息的结果,我们可以这样做

    public Object evaluateByJexl(String expression, Map<String,Object> context) {
        JexlEngine jexl = new JexlBuilder().create();
        JexlExpression e = jexl.createExpression(expression);
        JexlContext jc = new MapContext(context);
        return e.evaluate(jc);
    }
登入後複製

FreeMarker模板

<#if code == "200">
2
<#else>
3
<#/if>
登入後複製
登入後複製

处理这段模板我们可以这么做

    /**
     * 
     * @param param FreeMarker模板
     * @param context
     * @return
     * @throws Exception
     */
    public String render(String param, Map<String,Object> context) throws Exception {
        Configuration cfg = new Configuration();
        StringTemplateLoader stringLoader = new StringTemplateLoader();
        stringLoader.putTemplate("myTemplate",param);
        cfg.setTemplateLoader(stringLoader);
        Template template = cfg.getTemplate("myTemplate","utf-8");
        StringWriter writer = new StringWriter();
        template.process(context, writer);
        return writer.toString();
    }
登入後複製

如果FreeMarker模板比较复杂,从模板预编译成Template可能会消耗更多的性能,就要考虑把Template缓存起来。

JavaScript代码段

function handle(arg) {
    if(arg == 200) {
        return 2;
    }
    return 3;
}
handle(${code})
登入後複製

这段js代码中存在${code},首先它需要使用FreeMarker渲染得到真正的handle方法的调用参数,然后

    public Object evaluate(String expression) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");
        return engine.eval(expression);
    }
登入後複製

ScriptEngineManager的性能估计不太乐观,毕竟是一个语言的引擎。

不同转换规则实现的比较

类型实现优点缺点
三目表达式Jexl简单(easy)简单(simple)
FreeMarker模板FreeMarker----
JavaScript代码段FreeMarker + ScriptEngine直观过程复杂,性能问题

看起来Freemarker是一个不错的选择。
至此两步走小技巧已经实现了,都是利用了现成的代码实现。

或许我们会这样的挑战,在做状态值转换时需要知道当前系统某个业务状态值的情况,
此时Freemarker表达式可能是这样的,

<# assign lastCode = GetLastCode(code)>
<#if lastCode == "2">
2
<#elseif code == "200">
2
<#else>
3
<#/if>
登入後複製

这里我们可以使用Freemarker的特性,自定义Java函数或工具类,在模板中调用。

以上是解析XML和JSON內容的一點技巧的實例程式碼分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!