>백엔드 개발 >XML/RSS 튜토리얼 >XML 및 JSON 콘텐츠를 구문 분석하기 위한 일부 기술의 예제 코드 공유

XML 및 JSON 콘텐츠를 구문 분석하기 위한 일부 기술의 예제 코드 공유

黄舟
黄舟원래의
2017-03-17 17:08:111525검색

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을 설정할 수 있습니다.
JSONPath 표현식이 정의된다는 점을 제외하면 JSON 콘텐츠를 구문 분석하는 경우에도 마찬가지입니다.

두 단계로 데이터 콘텐츠 처리

원본 XML 및 JSON 데이터에서 bid, codedescription 정보를 얻고, 인터페이스 1에서
를 얻는다고 가정합니다. 인터페이스 2에서

{
    bid: &#39;16112638767472747178067&#39;,
    code: &#39;200&#39;,
    description: &#39;OK&#39;
}

Get

{
    bid: &#39;16112638767472747178068&#39;,
    code: &#39;1&#39;,
    description: &#39;成功&#39;
}

요청이 성공했음을 나타내기 위해 인터페이스 1 문서에서 상태 값 200을 가져오고, 인터페이스 1 문서에서 상태 값 1을 가져온다고 가정합니다. 인터페이스 2 문서는 요청이 성공했음을 나타내는 문서입니다. 모두 요청이 성공했다고 말했지만 여전히
비즈니스 관련 테이블에 그대로 저장할 수 없습니다(물론 이러한 응답 데이터는 여전히 적어도 문제 해결을 용이하게 하기 위해 또 다른 기록 테이블).
비즈니스 관련 테이블이 이렇게 디자인되었다고 가정해보세요

字段名 类型 描述
bid string 业务ID
code int 状态值,0=初始,1=请求中,2=成功,3=失败
description string 描述

因此,我们还必须定义规则把接口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函数的返回类型也可以是Mapd16797201dccc1fe78d0eb13fa114786,暂且用Map1384ae87b3aa4d7618d5d6d4b582a21b

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

这一步稍稍有点麻烦,不过我们先不考虑代码实现,反正你能想到的可能别人已经帮你实现了。首先我们根据接口文档定义规则,写出规则表达式(或者其他的什么),
又是表达式。假设接口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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.