新优特性之XML,JSon解析
Posted 开源中间件
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了新优特性之XML,JSon解析相关的知识,希望对你有一定的参考价值。
大家看到这个题目可能会问,XML解析还算新优特性么?
尽管XML解析的功能早在Java 1.2 / 1.3版本就已经加入了,但后续一直在不断完善。
Java9中加入了XML Catalogs功能,这是在XML处理中很重要的功能,符合我们对新优特性的定义。
但也就只有这一条,除此之外并没有其他XML功能加入,而且从Java11之后,JAXB功能还被从JDK中移除了。
Json格式的解析,其实并没有加入到JDK(JavaSE)之中。JavaEE7中加入JsonP,JavaEE8加入了JsonB的功能。
我们用专门一篇来讲XML和Json的解析,原因之一是格式文本的处理是非常重要的特性。另一个原因是,我们可以学习其不同层次的接口设计,以及在Java语言实现的演进过程。
一 XML
谈起XML的解析,可能有朋友会说,现在很少使用了。的确目前Web Service获取信息的方法基本都使用Rest和Json,然而XML作为语义丰富的格式语言,在很多应用中依然是关键的数据描述方式。
比如,一般的博客网站都会提供RSS或Atom的标题格式文本,就是一种特殊类型的XML。语义网描述文件RDF,也是一种XML,提供了针对数据的模型和语法,三元组模型可以很好的阐述语言中的主谓宾定义。
可以这么说,对于行业领域的数据,除了领域内特殊的数据格式以外,能够完整充分的描述其数据内容和类型关联的,XML可能是最佳选择了。
Java对XML的支持由来已久,被称为JAXP的功能是指对XML格式文件的解析,JAXB是在解析的基础上,转换为格式对象,XLST可以对XML进行数据转换。这些功能早已是JDK的一部分,放在javax.xml, org.w3c.dom, org.xm.sax等包名中。
1. XML解析和构造
可以分为三种方式,前两种是DOM和SAX,后一种StAX则是Java6加入的。
DOM(Document Object Model)是最基本的XML解析方法,通过DocumentBuilder.parser来解析XML文档,得到Document对象。
通过对Document对象的遍历得到整个文本模型,Document继承于Node,即一个XML节点,整个文档是一棵由多个XML Node组成的树。每一个元素Element构造出一个新的层次,Element可以有多个属性Attribute,同时可以构建元素的文本内容。
对于元素Element,有命名空间Namespace的概念,比如
<bs:Book xmlns:bs="http://www.books.org/bookstore">
这里bs就是命名空间,即使该XML和其他XML内容混用,也可以通过命名空间进行区分。
DOM方式把整个XML文件都装载到内存之中。但有些情况下,并不需要完整的解析整个文件,只需要处理一部分XML就可以了。
SAX解析方式,是读取XML,作为流Stream来处理。程序设置了各种事件的侦听器,当某个事件触发时,回调对应方法处理编写的程序逻辑。
解析方法是:
SAXParser.parse(File, DefaultHandler)
其中DefaultHandler是实现了ContentHandler接口的处理类,当解析器遇到一个元素定义时,startElement方法就会被回调触发,相应元素节点的数据,都通过方法参数传入。遇到元素结尾回调endElement方法,遇到元素中的文字characters方法被回调。
这种方式称之为“推”解析,即整个XML数据会从头到尾全部流过解析器,捕获处理每一个事件。可以用很少的内存就能处理很大的XML文件,但无法回溯查看前面的内容,也无法跳过不感兴趣的内容。
StAX方式对解析过程进行了改进,采用"拉"的方法。并不响应每一处事件,而只对选定的事件进行回调操作。
比如说气象局发布了全国天气预报的一个XML文件,我们只关心北京明天的天气。在解析时,可以过滤掉所有非北京的城市和其他日期的天气,只留下感兴趣的天气信息片段来处理。尽管整个数据流还是会通过解析器的,但因为跳过不感兴趣的信息段,解析程序就不需要进行填充回调方法参数的操作,这样就可以节约很多时间和资源。
从解析效率来说,一般情况下总是StAX方式最高,这也是我们对较大XML文件进行解析的首选方法。
DOM方式使用最简单,全部加载进行遍历即可,虽然需要全部加载到内存中,效率较低。
但DOM也有很多优势:
可以方便的对节点进行修改
多种遍历方式,深度优先,广度优先或者组合方式,利用强大的XPath表达式
更方便的进行XLST数据转换
2. XLST
XML作为树形数据格式,数据重新排列和转换是重要的功能。Java语言实现了W3C组织定义的XLST标准功能。
这部分代码在javax.xml.transform中,主要类Transformer中的transform(Source, Result)方法,把一个XML文档转换为另一个XML文档。
比如一个XML片段
<?xml version="1.0" encoding="UTF-8"?>
<BookStore>
<book id="1" name="Java Programming" price="79"/>
<book id="2" name="XML Programming" price="69"/>
<book id="3" name="XSLT Questions" price="71"/>
</BookStore>
利用这段XLST筛选代码
<xsl:template match="book">
<li><a href="viewBook?id={@id}">
<xsl:value-of select="@name"/>
</a></li>
</xsl:template>
可以得到如下XML(html)片段
<li><a href="viewBook?id=1">
Java Programming
</a></li>
<li><a href="viewBook?id=2">
XML Programming
</a></li>
<li><a href="viewBook?id=3">
XSLT Questions
</a></li>
3. XPath
XPath的作用是给定XML文档中信息的路径,其功能非常强大。
比如我们对于上面的XML,使用/BookStore/book/[@id="1"]就可以把编号为1的图书信息筛选出来。
如果想获得所有书的信息,使用/BookStore/book就可以。使用//book[@price>70]查询所有价格超过70的书籍。
还可以使用表达式,比如//book[last()]选取最后一本书。
使用XPath通过XLST对XML进行筛选,转换,组合,构造,可以生成需要的XML格式信息。
4. Validation
对于XML的验证,是通过XSD文件进行的。XSD也是一个XML文件,其中描述了XML结构的定义。
我们称XML为可校验的格式文本,就是指一个XML是可以被严格定义的。其中的节点组织的格式,使用哪些属性,元素的顺序,数据的类型,数值是多少,都可以定义和校验。
验证是通过javax.xml.validation包中,Validator类的validate方法实现。如果出现验证错误则抛出异常。
包在解析过程中,可以选择是否进行校验。
另外XML的验证也可以通过DTD格式进行,DTD是较早的格式定义,目前一般采用语法更丰富的XSD方式。
5. Catalogs
文章开头提过XML Catalogs,其实这个功能很早就加入到JDK之中,只是为内部类,JDK9时加入了javax.xml.catelog的包,这样成为了Java的公开接口和方法。
对于Catalogs,很多开发者并不是很了解,其具体作用是做文件的本地替换。
我们知道XML文件可以用XSD文件进行验证,也可以用include包含其他XML文件,对于XSD和XSL定义,也可以用import引入其他文件。
命名空间一般用一个不容易重复的字符串定义,URL就是一个很好的方法,比如
<xsd:include schemaLocation="http://openjdk.java.net/xsd/XSDInclude_person.xsd"/>
那么当XML解析到这里时,程序会尝试去网上下载这个文件,这样就会花时间,而且网络不通时程序就无法继续。
XML catalogs就能解决这个问题。比如给定一个catalog文件
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system systemId="http://openjdk.java.net/xsd/XSDInclude_person.xsd"
uri="xsd/XSDInclude_person.xsd"/>
</catalog>
以上告诉处理程序,如果遇到xsd文件,就到环境路径下查找本地文件xsd/XSDInclude_person.xsd,这样避免了网络问题,也会加快处理速度。
二 Json
Json是目前互联网传递信息最常见的方式,它比起XML要轻快很多,而且可以被javascript,即浏览器直接解析,所以被广泛使用。
但Java语言一直没有加入Json格式处理,作为JDK的一部分。
我们这篇讨论常用的方法。Java对Json的处理,目前采用最多的应该是Jackson这个三方库,当然还有Gson,国内的fastjson等方案。
1. Jackson
Jackson功能非常强大,不过最初使用时稍微麻烦,因为要引入3个jar文件,jackson-annotation, jackson-core, jackson-databind
但我们稍作分析就能明白这样划分的好处。
Annotation包中之容纳了各个注解,类似于JPA annotation包,只有注解文件
Core包里面是对Json的解析,是真正的格式内容解析器
Databind包中,是Json和JavaBean之间的转换,和XML对应的话就是JAXB的功能。
所以如果我们只看解析的话,只有Core包是我们需要关注的。
Jackson对于Json文件的解析,类似StAX的过程,通过JsonParser类,当Json的"元素"被解析时,会产生一个事件,转换为Jackson的JsonToken定义的枚举类型,如以下代码片段
try {
JsonFactory jfactory = new JsonFactory();
JsonParser jParser = jfactory.createJsonParser(new File("user.json"));
while (jParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jParser.getCurrentName();
if ("name".equals(fieldname)) {
jParser.nextToken();
System.out.println(jParser.getText());
}
}
jParser.close();
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
databind则执行Json到JavaBean的转换,通过类ObjectMapper完成。
2. JsonP
在JavaEE7发布时,加入了对Json包解析的一个实现,采用javax.json包名。
所以也可以看作是“准官方”的Json解析工具。
这套API定义非常清爽,有若干个接口和类
Json是入口点,提供了静态工厂
JsonGenerator, JsonWriter生成Json格式文件
JsonReader读取Json文本
JsonObject, JsonArray, JsonString, JsonNumber等表示Json内部组成,都是JsonValue的子类
读取Json文件时,如果已知类型格式,可采用逐项遍历的方法,如:
URL url = new URL("https://mydata.com");
try (InputStream is = url.openStream();
JsonReader rdr = Json.createReader(is)) {
JsonObject obj = rdr.readObject();
JsonArray results = obj.getJsonArray("data");
for (JsonObject result : results.getValuesAs(JsonObject.class)) {
System.out.print(result.getJsonObject("from").getString("name"));
System.out.print(": ");
System.out.println(result.getString("message", ""));
}
}
这段程序知道Json格式包含数组类型,数组每一项获取from类型的对象,再把name和message对应的信息打印出来。
这样的解析方式类似于DOM,即把整个Json读入到内存中。
另外一种解析遍历方式是利用流的方法:
URL url = new URL("https://mydata.com");
try (InputStream is = url.openStream();
JsonParser parser = Json.createParser(is)) {
while (parser.hasNext()) {
Event e = parser.next();
if (e == Event.KEY_NAME) {
switch (parser.getString()) {
case "name":
parser.next();
System.out.print(parser.getString());
System.out.print(": ");
break;
case "message":
parser.next();
System.out.println(parser.getString());
System.out.println("---------");
break;
}
}
}
}
这段代码解析Json采用了类似SAX的方法,遇到了某个事件,进行解析处理。此处是判断是否为键名称,获取下一个信息就是数据内容。
JsonP以较少的代码量精巧的实现了Json的解析,同时配合JsonB,可以操作Json文本和Java对象之间的转换。
对于用JavaEE开发技术开发应用,是推荐使用的Json解析方式。
我们这一讲着重在解析文本,就不展开讲Binding映射到对象的过程了,如JAXB和JsonB。
Json格式简单,便于使用,但受到其语义的限制,难以进行复杂的操作,比如进行验证,数据转换等。当然,我们可以通过在格式中加入相关信息实现这些功能,但已经不是Json自身的功能,而是扩展实现的功能。
通过对XML,Json这两种常用的格式文本解析的说明,我们可以了解格式文本的若干方法。尽管格式不同,解析方法却是有相似之处的。
对于其他格式文本,如yaml,ini等文本,也是采用差不多的思路。
文本处理是软件开发中最常用的功能,所以我们一定要对常用接口非常熟悉,并针对不同尺寸的文本和应用类型,选用不同的解析方法,做到效率最佳。
以上是关于新优特性之XML,JSon解析的主要内容,如果未能解决你的问题,请参考以下文章