En l'absence d'un standard Lorsqu'un système se connecte à plusieurs systèmes externes, il rencontre souvent des données de réponse hétérogènes provenant de l'interface de requête. Il peut renvoyer du XML ou du
JSON. Outre les différents types de retour, la structure du contenu est également différente. En prenant le type XML comme exemple,
L'interface 1 renvoie le contenu
<root> <bizKey>16112638767472747178067</bizKey> <returnMsg>OK</returnMsg> <returnCode>200</returnCode> ... </root>
L'interface 2 renvoie le contenu
<root> <bid>16112638767472747178068</bid> <note>成功</note> <returnStatus>1</returnStatus> ... </root>
Si le contenu de chaque format est traité dans notre système, il est évidemment déraisonnable. Dans le contenu ci-dessus, nous ne nous soucions que de trois types d'informations, à savoir l'identifiant de l'entreprise, la valeur de statut et les informations de description
Après avoir obtenu ces informations, nous pouvons effectuer une logique métier ? traitement.
Selon l'abstraction métier, nous devons obtenir trois types d'informations à partir du contenu XML ou JSON. Nous utiliserons XPath et JSONPath pour analyser ici. . Par exemple, pour obtenir les informations importantes de l'interface 1,
on peut définir trois expressions XPath,
{ bid: "/root/bizKey", code: "/root/returnCode", description: "/root/returnMsg" }
bid
, code
et description
correspondant aux noms de champs définis par notre système.
Il en va de même pour l'analyse du contenu JSON, sauf que l'expression JSONPath est définie.
Supposons que nous obtenions les informations bid
, code
et description
à partir des données XML et JSON d'origine,
à partir de l'interface 1 Get
{ bid: '16112638767472747178067', code: '200', description: 'OK' }
de l'interface 2 Obtenez
{ bid: '16112638767472747178068', code: '1', description: '成功' }
Supposons que nous obtenions la valeur d'état 200
du document de l'interface 1 pour indiquer que la demande est réussie, et obtenons la valeur d'état du document d'interface 2 1
pour indiquer La demande est réussie. Bien qu'ils indiquent tous que la demande est réussie, nous ne pouvons toujours pas
les enregistrer intacts dans nos tables liées à l'entreprise (bien sûr, ces données de réponse doivent toujours être enregistrées. dans une autre table d'enregistrement, au moins pour faciliter le dépannage) .
Supposons que nos tables professionnelles soient conçues comme ceci
字段名 | 类型 | 描述 |
---|---|---|
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
函数的返回类型也可以是Map<String,String>
,暂且用Map<String,Object>
。
这一步稍稍有点麻烦,不过我们先不考虑代码实现,反正你能想到的可能别人已经帮你实现了。首先我们根据接口文档定义规则,写出规则表达式(或者其他的什么),
又是表达式。假设接口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函数或工具类,在模板中调用。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!