를 구문 분석하는 작은 트릭 표준 시스템이 여러 외부 시스템에 연결되면 요청 인터페이스에서 이기종 응답 데이터가 발생하는 경우가 종종 있습니다. 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 콘텐츠에서 세 가지 유형의 정보를 가져와야 합니다. 여기서는 XPath 및 JSONPath를 사용하여 구문 분석합니다. . 예를 들어 인터페이스 1의 중요한 정보를 얻으려면
시스템에서 정의한 필드 이름에 해당하는 세 가지 XPath 표현식
{ bid: "/root/bizKey", code: "/root/returnCode", description: "/root/returnMsg" }
bid
, code
및 description
을 설정할 수 있습니다.
JSONPath 표현식이 정의된다는 점을 제외하면 JSON 콘텐츠를 구문 분석하는 경우에도 마찬가지입니다.
원본 XML 및 JSON 데이터에서 bid
, code
및 description
정보를 얻고, 인터페이스 1에서
를 얻는다고 가정합니다. 인터페이스 2에서
{ bid: '16112638767472747178067', code: '200', description: 'OK' }
Get
{ bid: '16112638767472747178068', code: '1', description: '成功' }
요청이 성공했음을 나타내기 위해 인터페이스 1 문서에서 상태 값 200
을 가져오고, 인터페이스 1 문서에서 상태 값 1
을 가져온다고 가정합니다. 인터페이스 2 문서는 요청이 성공했음을 나타내는 문서입니다. 모두 요청이 성공했다고 말했지만 여전히
비즈니스 관련 테이블에 그대로 저장할 수 없습니다(물론 이러한 응답 데이터는 여전히 적어도 문제 해결을 용이하게 하기 위해 또 다른 기록 테이블).
비즈니스 관련 테이블이 이렇게 디자인되었다고 가정해보세요
字段名 | 类型 | 描述 |
---|---|---|
bid | string | 业务ID |
code | int | 状态值,0=初始,1=请求中,2=成功,3=失败 |
description | string | 描述 |
因此,我们还必须定义规则把接口1返回的状态值200
转换为我们系统的2
,把接口2返回的状态值1
转换为我们系统的2
。
总结一下,两步走解析XML和JSON数据内容
根据XPath或者JSONPath表达式解析获得重要信息
根据规则转换状态值
以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("200") ? 2: 3
或者
<#if code == "200"> 2 <#else> 3 <#/if>
亦或者
function handle(arg) { if(arg == 200) { return 2; } return 3; } handle(${code})
以上根据同一份文档定义了三种不同类型的状态值转换规则,肯定需要三种不同的实现。下面一一说明,
code.equals("200") ? 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); }
<#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
缓存起来。
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!