新优特性之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解析的主要内容,如果未能解决你的问题,请参考以下文章

OC之JSON数据解析

IOS 网络浅析-(七 JSON解析之三方JSONKit)

iOS开发之JSON格式数据的生成与解析

网络请求之JSON解析

iOS开发之网络数据解析--JSON解析简介

Libgdx之JSON文件解析