溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

解析XML和JSON內(nèi)容的技巧有哪些

發(fā)布時間:2021-09-17 14:13:40 來源:億速云 閱讀:148 作者:小新 欄目:編程語言

這篇文章主要介紹解析XML和JSON內(nèi)容的技巧有哪些,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

解析XML和JSON內(nèi)容的一點技巧

概述

在沒有統(tǒng)一標(biāo)準(zhǔn)的情況下,一個系統(tǒng)對接多個外部系統(tǒng)往往會遇到請求接口響應(yīng)數(shù)據(jù)異構(gòu)的情況,有可能返回的是XML,也有可能返回
JSON。除了返回類型不同,內(nèi)容結(jié)構(gòu)也不盡相同。以XML類型為例,
接口1返回內(nèi)容

<root>
    <bizKey>16112638767472747178067</bizKey>
    <returnMsg>OK</returnMsg>
    <returnCode>200</returnCode>
    ...
</root>

接口2返回內(nèi)容

<root>
    <bid>16112638767472747178068</bid>
    <note>成功</note>
    <returnStatus>1</returnStatus>
    ...
</root>

如果在我們系統(tǒng)中為每種格式的內(nèi)容針對處理顯然是不合理的,上面的內(nèi)容中我們只是關(guān)心三種信息,分別是業(yè)務(wù)ID、狀態(tài)值和描述信息,那么可不可以抽象這三種信息,
獲得這些信息后再進行業(yè)務(wù)邏輯處理。

解析XML和JSON

根據(jù)業(yè)務(wù)抽象我們需要從XML或者JSON內(nèi)容中獲得三種信息,我們這里將會使用XPath和JSONPath的方式來解析。比如獲得接口1的重要信息,
我們可以設(shè)定三個XPath表達式,

{
    bid: "/root/bizKey",
    code: "/root/returnCode",
    description: "/root/returnMsg"
}

bid,codedescription對應(yīng)我們系統(tǒng)自己定義的字段名。
解析JSON內(nèi)容也是同理的,只不過定義的是JSONPath表達式。

分兩步走處理數(shù)據(jù)內(nèi)容

假設(shè)我們從原始的XML和JSON數(shù)據(jù)中獲得了bid,codedescription信息,
從接口1獲得

{
    bid: '16112638767472747178067',
    code: '200',
    description: 'OK'
}

從接口2獲得

{
    bid: '16112638767472747178068',
    code: '1',
    description: '成功'
}

假設(shè)我們從接口1文檔獲知狀態(tài)值200表示請求成功,從接口2文檔獲知狀態(tài)值1表示請求成功,雖然他們都表示請求成功,但是我們還是不能
把他們原原本本地保存到我們的業(yè)務(wù)相關(guān)表中(當(dāng)然這些響應(yīng)數(shù)據(jù)還是需要保存到另外的記錄表中的,至少方便排查問題)。
假設(shè)我們的業(yè)務(wù)相關(guān)表是這樣設(shè)計的

字段名類型描述
bidstring業(yè)務(wù)ID
codeint狀態(tài)值,0=初始,1=請求中,2=成功,3=失敗
descriptionstring描述

因此,我們還必須定義規(guī)則把接口1返回的狀態(tài)值200轉(zhuǎn)換為我們系統(tǒng)的2,把接口2返回的狀態(tài)值1轉(zhuǎn)換為我們系統(tǒng)的2。
總結(jié)一下,兩步走解析XML和JSON數(shù)據(jù)內(nèi)容

  1. 根據(jù)XPath或者JSONPath表達式解析獲得重要信息

  2. 根據(jù)規(guī)則轉(zhuǎn)換狀態(tài)值

第一步解析數(shù)據(jù)獲得重要信息

以XML為例,

public class XmlParseUtils {
    private DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    private XPathFactory xpathFactory = XPathFactory.newInstance();
    
    /**
     * 
     * @param param    數(shù)據(jù)內(nèi)容
     * @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函數(shù)的返回類型也可以是Map<String,String>,暫且用Map<String,Object>。

第二步根據(jù)規(guī)則轉(zhuǎn)換狀態(tài)值

這一步稍稍有點麻煩,不過我們先不考慮代碼實現(xiàn),反正你能想到的可能別人已經(jīng)幫你實現(xiàn)了。首先我們根據(jù)接口文檔定義規(guī)則,寫出規(guī)則表達式(或者其他的什么),
又是表達式。假設(shè)接口1的返回的狀態(tài)值比較簡單,只有200表示成功,其他情況都是失敗,那么我們可以這樣定義規(guī)則,

code.equals("200") ? 2: 3

或者

<#if code == "200">
2
<#else>
3
<#/if>

亦或者

function handle(arg) {
    if(arg == 200) {
        return 2;
    }
    return 3;
}
handle(${code})

以上根據(jù)同一份文檔定義了三種不同類型的狀態(tài)值轉(zhuǎn)換規(guī)則,肯定需要三種不同的實現(xiàn)。下面一一說明,

三目表達式

code.equals("200") ? 2: 3是一個三目表達式,我們將使用jexl引擎來解析,利用第一步解析數(shù)據(jù)獲得重要信息的結(jié)果,我們可以這樣做

    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模板比較復(fù)雜,從模板預(yù)編譯成Template可能會消耗更多的性能,就要考慮把Template緩存起來。

JavaScript代碼段

function handle(arg) {
    if(arg == 200) {
        return 2;
    }
    return 3;
}
handle(${code})

這段js代碼中存在${code},首先它需要使用FreeMarker渲染得到真正的handle方法的調(diào)用參數(shù),然后

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

ScriptEngineManager的性能估計不太樂觀,畢竟是一個語言的引擎。

不同轉(zhuǎn)換規(guī)則實現(xiàn)的比較

類型實現(xiàn)優(yōu)點缺點
三目表達式Jexl簡單(easy)簡單(simple)
FreeMarker模板FreeMarker----
JavaScript代碼段FreeMarker + ScriptEngine直觀過程復(fù)雜,性能問題

看起來Freemarker是一個不錯的選擇。
至此兩步走小技巧已經(jīng)實現(xiàn)了,都是利用了現(xiàn)成的代碼實現(xiàn)。

或許我們會這樣的挑戰(zhàn),在做狀態(tài)值轉(zhuǎn)換時需要知道當(dāng)前系統(tǒng)某個業(yè)務(wù)狀態(tài)值的情況,
此時Freemarker表達式可能是這樣的,

<# assign lastCode = GetLastCode(code)>
<#if lastCode == "2">
2
<#elseif code == "200">
2
<#else>
3
<#/if>

這里我們可以使用Freemarker的特性,自定義Java函數(shù)或工具類,在模板中調(diào)用。

以上是“解析XML和JSON內(nèi)容的技巧有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI