摘要:解析和内容的一点技巧概述在没有统一标准的情况下,一个系统对接多个外部系统往往会遇到请求接口响应数据异构的情况,有可能返回的是,也有可能返回。解析内容也是同理的,只不过定义的是表达式。
解析XML和JSON内容的一点技巧 概述
在没有统一标准的情况下,一个系统对接多个外部系统往往会遇到请求接口响应数据异构的情况,有可能返回的是XML,也有可能返回
JSON。除了返回类型不同,内容结构也不尽相同。以XML类型为例,
接口1返回内容
</>复制代码
16112638767472747178067
OK
200 ...
接口2返回内容
</>复制代码
16112638767472747178068
成功
1 ...
如果在我们系统中为每种格式的内容针对处理显然是不合理的,上面的内容中我们只是关心三种信息,分别是业务ID、状态值和描述信息,那么可不可以抽象这三种信息,
获得这些信息后再进行业务逻辑处理。
根据业务抽象我们需要从XML或者JSON内容中获得三种信息,我们这里将会使用XPath和JSONPath的方式来解析。比如获得接口1的重要信息,
我们可以设定三个XPath表达式,
</>复制代码
{
bid: "/root/bizKey",
code: "/root/returnCode",
description: "/root/returnMsg"
}
bid,code和description对应我们系统自己定义的字段名。
解析JSON内容也是同理的,只不过定义的是JSONPath表达式。
假设我们从原始的XML和JSON数据中获得了bid,code和description信息,
从接口1获得
</>复制代码
{
bid: "16112638767472747178067",
code: "200",
description: "OK"
}
从接口2获得
</>复制代码
{
bid: "16112638767472747178068",
code: "1",
description: "成功"
}
假设我们从接口1文档获知状态值200表示请求成功,从接口2文档获知状态值1表示请求成功,虽然他们都表示请求成功,但是我们还是不能
把他们原原本本地保存到我们的业务相关表中(当然这些响应数据还是需要保存到另外的记录表中的,至少方便排查问题)。
假设我们的业务相关表是这样设计的
字段名 | 类型 | 描述 |
---|---|---|
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 parse(String param, Map paths) throws Exception{
InputSource inputSource = new InputSource(new StringReader(param));
Document document = dbFactory.newDocumentBuilder().parse(inputSource);
Map 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
这一步稍稍有点麻烦,不过我们先不考虑代码实现,反正你能想到的可能别人已经帮你实现了。首先我们根据接口文档定义规则,写出规则表达式(或者其他的什么),
又是表达式。假设接口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 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 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函数或工具类,在模板中调用。
代码地址https://github.com/Honwhy/xml...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/110361.html
摘要:解析和内容的一点技巧概述在没有统一标准的情况下,一个系统对接多个外部系统往往会遇到请求接口响应数据异构的情况,有可能返回的是,也有可能返回。解析内容也是同理的,只不过定义的是表达式。 解析XML和JSON内容的一点技巧 概述 在没有统一标准的情况下,一个系统对接多个外部系统往往会遇到请求接口响应数据异构的情况,有可能返回的是XML,也有可能返回JSON。除了返回类型不同,内容结构也不尽...
摘要:使用的思考使用过对象的程序员最常做的一项工作便是,将对象转化为字符串。该字符串的用途很多,例如可以使用在的中,在多个页面间进行传递。因为模式是固定的,那么定义模式的空间开销一定比使用该模式生成字符串的时间开销代价要小很多。 使用JSON.stringify的思考 使用过JSON对象的程序员最常做的一项工作便是,将JSON对象转化为字符串。该字符串的用途很多,例如可以使用在WEB的URL...
摘要:学编程真的不是一件容易的事不管你多喜欢或是多会编程,在学习和解决问题上总会碰到障碍。熟练掌握核心内容,特别是和多线程初步具备面向对象设计和编程的能力掌握基本的优化策略。 学Java编程真的不是一件容易的事,不管你多喜欢或是多会Java编程,在学习和解决问题上总会碰到障碍。工作的时间越久就越能明白这个道理。不过这倒是一个让人进步的机会,因为你要一直不断的学习才能很好的解决你面前的难题...
阅读 834·2021-11-23 09:51
阅读 3371·2019-08-30 15:54
阅读 519·2019-08-30 15:52
阅读 3192·2019-08-30 13:58
阅读 3003·2019-08-30 13:53
阅读 2755·2019-08-29 14:18
阅读 2511·2019-08-27 10:54
阅读 2426·2019-08-26 18:09
极致性价比!云服务器续费无忧!
Tesla A100/A800、Tesla V100S等多种GPU云主机特惠2折起,不限台数,续费同价。
NVIDIA RTX 40系,高性价比推理显卡,满足AI应用场景需要。
乌兰察布+上海青浦,满足东推西训AI场景需要