前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java XML和JSON:Java SE的文档处理,第1部分

Java XML和JSON:Java SE的文档处理,第1部分

作者头像
银河1号
发布2019-04-12 09:45:04
5.6K0
发布2019-04-12 09:45:04
举报
文章被收录于专栏:银河系资讯银河系资讯

XML和JSON对我很重要,我很感谢Apress允许我写一本关于它们的书。在这篇Java Q&A文章中,我将简要介绍我的新书第二版,Java XML和JSON。我还将提供两个有用的演示,如果我有足够的空间,我本来希望将其包括在书中。

首先,我将向您展示如何覆盖Xalan,它是Java 11的标准XSLT实现,具有XSLT 2.0+和XPath 2.0 +兼容的替代方案,在本例中为SAXON。使用SAXON for XSLT / XPath可以更轻松地访问分组等功能,我还将演示。接下来,我将向您展示使用Jackson将XML转换为JSON的两种方法:第一种技术是数据绑定,第二种是树遍历。

为什么选择XML和JSON?

在XML到来之前,我编写了软件来导入以未记录的二进制格式存储的数据。我使用调试器来识别数据字段类型,文件偏移量和长度。当XML出现,然后是JSON时,这项技术大大简化了我的生活。

第一版Java XML和JSON(2016年6月)介绍了XML和JSON,探讨了Java SE自己的面向XML的API,并探讨了面向Java SE的外部面向JSON的API。最近由Apress发布的第二版提供了新内容,并且(希望)回答了有关XML,JSON,Java SE的XML API和各种JSON API(包括JSON-P)的更多问题。它也针对Java SE 11进行了更新。

在写完这本书后,我分别写了两个部分,分别介绍了SAXON和Jackson的有用功能。我将在这篇文章中介绍这些部分。首先,我将花一点时间介绍这本书及其内容。

Java XML和JSON,第二版

理想情况下,在研究本文中的其他内容之前,您应该阅读第二版Java XML和JSON。即使您尚未阅读本书,您也应该知道它涵盖的内容,因为该信息会将其他部分放在上下文中。

第二版Java XML和JSON分为三个部分,包括12章和附录:

  • 第1部分:探索XML
  • 第1章:XML简介第
  • 2章:使用SAX解析XML文档
  • 第3章:使用DOM解析和创建XML文档
  • 第4章:使用StAX解析和创建XML文档
  • 第5章:使用XPath选择节点
  • 第6章:使用XSLT转换XML文档
  • 第2部分:探索JSON
  • 第7章:JSON简介
  • 第8章:使用mJson解析和创建JSON对象
  • 第9章:使用Gson解析和创建JSON对象
  • 第10章:使用JsonPath提取JSON值
  • 第11章:使用Jackson处理JSON第12章:使用JSON-P处理JSON
  • 第3部分:附录附录A:练习答案

第1部分侧重于XML。第1章定义了关键术语,介绍了XML语言特性(XML声明,元素和属性,字符引用和CDATA部分,命名空间,注释和处理指令),并介绍了XML文档验证(通过文档类型定义和模式)。其余五章探讨了Java SE的SAX,DOM,StAX,XPath和XSLT API。

第1部分侧重于XML。第1章定义了关键术语,介绍了XML语言特性(XML声明,元素和属性,字符引用和CDATA部分,命名空间,注释和处理指令),并介绍了XML文档验证(通过文档类型定义和模式)。其余五章探讨了Java SE的SAX,DOM,StAX,XPath和XSLT API。

第2部分重点介绍JSON。第7章定义了关键术语,浏览JSON语法,在JavaScript上下文中演示JSON(因为Java SE尚未正式支持JSON),并展示了如何验证JSON对象(通过JSON Schema Validator在线工具)。其余五章探讨第三方mJSon,Gson,JsonPath和Jackson API; 和Oracle面向Java EE的JSON-P API,它也可以在Java SE上下文中非正式使用。

每一章都以一系列练习结束,包括编程练习,旨在加强读者对材料的理解。答案在书的附录中公布。

新版本在某些重要方面与其前身不同:

  • 第2章介绍了获取XML阅读器的正确方法。上一版的方法已被弃用。
  • 第3章还介绍了DOM的加载和保存,范围和遍历API。
  • 第6章介绍了如何使用SAXON超越XSLT / XPath 1.0。
  • 第11章是探索杰克逊的一个新的(冗长的)章节。
  • 第12章是探索JSON-P的新(冗长)章节。

此版本还纠正了上一版内容中的小错误,更新了各种数字,并添加了许多新练习。

虽然我在第二版中没有空间,但Java XML和JSON的未来版本可能涵盖YAML。

第6章附录:使用XSLT转换XML文档

使用SAXON超越XSLT / XPath 1.0

Java 11的XSLT实现基于Apache Xalan Project,它支持XSLT 1.0和XPath 1.0,但仅限于这些早期版本。要访问以后的XSLT 2.0+和XPath 2.0+功能,您需要使用SAXON等替代方法覆盖Xalan实现。

Java XML和JSON,第6章介绍了如何使用SAXON覆盖Xalan,然后验证是否正在使用SAXON。在演示中,我建议在应用程序的main()方法开头插入以下行,以便使用SAXON:

代码语言:javascript
复制
System.setProperty("javax.xml.transform.TransformerFactory",
                   "net.sf.saxon.TransformerFactoryImpl");

您实际上不需要此方法调用,因为SAXON的TransformerFactory实现在JAR文件中作为服务提供,当通过类路径访问JAR文件时,该服务会自动加载。但是,如果TransformerFactory类路径上有多个实现JAR文件,并且Java运行时选择非SAXON服务作为转换器实现,则可能存在问题。包括上述方法调用将覆盖SAXON的选择。

XSLT / XPath功能:一个演示

第6章介绍了两个XSLTDemo应用程序,第三个应用程序可以在本书的代码存档中找到。下面的清单1提供了第四个XSLTDemo演示应用程序,它突出了XSLT / XPath功能。

清单1. XSLTDemo.java

代码语言:javascript
复制
import java.io.FileReader;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;

import org.xml.sax.SAXException;

import static java.lang.System.*;

public class XSLTDemo
{
   public static void main(String[] args)
   {
      if (args.length != 2)
      {
         err.println("usage: java XSLTDemo xmlfile xslfile");
         return;
      }

      try
      {
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         DocumentBuilder db = dbf.newDocumentBuilder();
         Document doc = db.parse(args[0]);
         TransformerFactory tf = TransformerFactory.newInstance();
         out.printf("TransformerFactory: %s%n", tf);
         FileReader fr = new FileReader(args[1]);
         StreamSource ssStyleSheet = new StreamSource(fr);
         Transformer t = tf.newTransformer(ssStyleSheet);
         Source source = new DOMSource(doc);
         Result result = new StreamResult(out);
         t.transform(source, result);
      }
      catch (IOException ioe)
      {
         err.printf("IOE: %s%n", ioe.toString());
      }
      catch (FactoryConfigurationError fce)
      {
         err.printf("FCE: %s%n", fce.toString());
      }
      catch (ParserConfigurationException pce)
      {
         err.printf("PCE: %s%n", pce.toString());
      }
      catch (SAXException saxe)
      {
         err.printf("SAXE: %s%n", saxe.toString());
      }
      catch (TransformerConfigurationException tce)
      {
         err.printf("TCE: %s%n", tce.toString());
      }
      catch (TransformerException te)
      {
         err.printf("TE: %s%n", te.toString());
      }
      catch (TransformerFactoryConfigurationError tfce)
      {
         err.printf("TFCE: %s%n", tfce.toString());
      }
   }
}

清单1中的代码类似于第6章的清单6-2,但是存在一些差异。首先,main()必须使用两个命令行参数调用清单1的方法:第一个参数命名XML文件; 第二个参数命名XSL文件。

第二个区别是我没有在变压器上设置任何输出属性。具体来说,我没有指定输出方法或是否使用缩进。这些任务可以在XSL文件中完成。

编译清单1如下:

代码语言:javascript
复制
javac XSLTDemo.java

XSLT 2.0示例:对节点进行分组

XSLT 1.0不提供对分组节点的内置支持。例如,您可能希望转换以下XML文档,该文档列出了作者的书籍:

代码语言:javascript
复制
<book title="Book 1">
  <author name="Author 1" />
  <author name="Author 2" />
</book>
<book title="Book 2">
  <author name="Author 1" />
</book>
<book title="Book 3">
  <author name="Author 2" />
  <author name="Author 3" />
</book>

进入以下XML,其中列出了作者的书籍:

代码语言:javascript
复制
<author name="Author 1">
  <book title="Book 1" />
  <book title="Book 2" />
</author>
<author name="Author 2">
  <book title="Book 1" />
  <book title="Book 3" />
</author>
<author name="Author 3">
  <book title="Book 3" />
</author>

虽然这种转换在XSLT 1.0中是可行的,但它很尴尬。xsl:for-each-group相比之下,XSLT 2.0的元素允许您获取一组节点,按某些标准对其进行分组,并处理每个创建的组。

让我们从要处理的XML文档开始探索此功能。清单2显示了books.xml按书名对作者姓名进行分组的文件的内容。

清单2. books.xml(按书名分组)

代码语言:javascript
复制
<?xml version="1.0"?>
<books>
   <book title="Securing Office 365: Masterminding MDM and Compliance in the Cloud">
     <author name="Matthew Katzer"/>
     <publisher name="Apress" isbn="978-1484242292" pubyear="2019"/>
   </book>
   <book title="Office 2019 For Dummies">
     <author name="Wallace Wang"/>
     <publisher name="For Dummies" isbn="978-1119513988" pubyear="2018"/>
   </book>
   <book title="Office 365: Migrating and Managing Your Business in the Cloud">
     <author name="Matthew Katzer"/>
     <author name="Don Crawford"/>
     <publisher name="Apress" isbn="978-1430265269" pubyear="2014"/>
   </book>
</books>

清单3显示了一个books.xsl文件的内容,该文件提供了XSL转换,可以将此文档转换为根据作者名称对书名进行分组的文档。

清单3. books.xsl(按作者姓名分组)

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">
  <xsl:output method="html" indent="yes"/>
  <xsl:template match="/books">
<html>
<head>
</head>
<body>
      <xsl:for-each-group select="book/author" group-by="@name">
      <xsl:sort select="@name"/>
<author name="{@name}">
          <xsl:for-each select="current-group()">
          <xsl:sort select="../@title"/>
<book title="{../@title}" />
          </xsl:for-each>
</author>
      </xsl:for-each-group>
</body>
</html>
  </xsl:template>
</xsl:stylesheet>

xsl:output元素表示需要缩进的HTML输出。的xsl:template-match元件的单相匹配books根元素。

xsl:for-each-group元素选择一系列节点并将它们组织成。该select属性是一个XPath表达式,用于标识要分组的元素。在这里,它被告知选择author属于book元素的所有元素。该group-by属性将具有相同值的所有元素组合在一起,分组键恰好是元素的@name属性author。实质上,您最终得到以下组:

代码语言:javascript
复制
Group 1

Matthew Katzer
Matthew Katzer

Group 2

Wallace Wang

Group 3

Don Crawford

这些组不是作者姓名的字母顺序,因此author将输出元素,这Matthew Katzer是第一个Don Crawford也是最后一个。该xsl:sort select="@name"元素确保author元素按排序顺序输出。

<author name="{@name}">构造输出一个<author>标签,其name属性仅分配给组中的第一个作者名称。

继续,xsl:for-each select="current-group()"迭代当前for-each-group迭代组中的作者姓名。该xsl:sort select="../@title"构造将根据书名对book通过后续<book title="{../@title}" />构造指定的输出元素进行排序

Transformation

现在让我们尝试转型。执行以下命令:

代码语言:javascript
复制
java XSLTDemo books.xml books.xsl

遗憾的是,此转换失败:您应该观察将Apache Xalan标识为变换器工厂的输出以及声明xsl:for-each-group不支持的错误消息。

让我们再试一次。假设saxon9he.jar并且XSLTDemo.class位于当前目录中,请执行以下命令:

代码语言:javascript
复制
java -cp saxon9he.jar;. XSLTDemo books.xml books.xsl

这一次,您应该观察以下排序和正确分组的输出:

代码语言:javascript
复制
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   </head>
   <body>
      <author name="Don Crawford">
         <book title="Office 365: Migrating and Managing Your Business in the Cloud"></book>
      </author>
      <author name="Matthew Katzer">
         <book title="Office 365: Migrating and Managing Your Business in the Cloud"></book>
         <book title="Securing Office 365: Masterminding MDM and Compliance in the Cloud"></book>
      </author>
      <author name="Wallace Wang">
         <book title="Office 2019 For Dummies"></book>
      </author>
   </body>
</html>

第11章附录:与Jackon一起处理JSON

使用Jackson将XML转换为JSON

Java XML和JSON,第11章介绍了Jackson,它提供了用于解析和创建JSON对象的API。也可以使用Jackson将XML文档转换为JSON文档。

在本节中,我将向您展示将XML转换为JSON的两种方法,首先是数据绑定,然后是树遍历。我假设你已经读过第11章并熟悉杰克逊。为了遵循这些演示,您应该从Maven存储库下载以下JAR文件:

  • jackson-annotations-2.9.7.jar
  • jackson-core-2.9.7.jar
  • jackson-databind-2.9.7.jar

您还需要一些额外的JAR文件; 大多数转换技术都很常见。我将尽快提供有关获取这些JAR文件的信息。

使用数据绑定将XML转换为JSON

数据绑定允许您将序列化数据映射到Java对象。例如,假设您有一个描述单个行星的小型XML文档。清单4给出了这个文档。

清单4. planet.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<planet>
    <name>Earth</name>
    <planet_from_sun>3</planet_from_sun>
    <moons>9</moons>
</planet>

清单5展示了一个等效的Java Planet类,其对象映射到了planet.xml内容。

清单5. Planet.java

代码语言:javascript
复制
public class Planet
{
   public String name;
   public Integer planet_from_sun;
   public Integer moons;
}

转换过程要求您首先将XML解析为Planet对象。您可以通过使用com.fasterxml.jackson.dataformat.xml.XmlMapper该类来完成此任务,如下所示:

代码语言:javascript
复制
XmlMapper xmlMapper = new XmlMapper();
XMLInputFactory xmlif = XMLInputFactory.newFactory();
FileReader fr = new FileReader("planet.xml");
XMLStreamReader xmlsr = xmlif.createXMLStreamReader(fr);
Planet planet = xmlMapper.readValue(xmlsr, Planet.class);

XmlMapper是一个com.fasterxml.jackson.databind.ObjectMapper读取和写入XML 的自定义。它提供了几种readValue()从特定于XML的输入源读取单个XML值的方法; 例如:

代码语言:javascript
复制
<T> T readValue(XMLStreamReader r, Class<T> valueType)

每个readValue()方法都需要一个javax.xml.stream.XMLStreamReader对象作为其第一个参数。该对象本质上是一个基于StAX的基于流的解析器,用于以前向方式有效地解析文本。

第二个参数是java.lang.Class正在实例化的目标类型的对象,填充了XML数据,随后从该方法返回其实例。

这段代码片段的底线是清单4的内容被读入一个返回给它的调用者的Planet对象readValue()

一旦创建了对象,就可以通过使用ObjectMapper它的String writeValueAsString(Object value)方法将其写成JSON :

代码语言:javascript
复制
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writeValueAsString(planet);

我从一个XML2JSON完整源代码如清单6所示的应用程序中摘录了这些代码片段。

清单6. XML2JSON.java(版本1)

代码语言:javascript
复制
import java.io.FileReader;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import static java.lang.System.*;

public class XML2JSON
{
   public static void main(String[] args) throws Exception
   {
      XmlMapper xmlMapper = new XmlMapper();
      XMLInputFactory xmlif = XMLInputFactory.newFactory();
      FileReader fr = new FileReader("planet.xml");
      XMLStreamReader xmlsr = xmlif.createXMLStreamReader(fr);
      Planet planet = xmlMapper.readValue(xmlsr, Planet.class);
      ObjectMapper jsonMapper = new ObjectMapper();
      String json = jsonMapper.writeValueAsString(planet);
      out.println(json);
   }
}

之前,你可以编译清单5和6,你需要下载杰克逊DATAFORMAT XML,它实现XMLMapper。我下载了2.9.7版,与其他三个Jackson软件包的版本相匹配。

假设您已成功下载jackson-dataformat-xml-2.9.7.jar,请执行以下命令(分为两行以便于阅读)以编译源代码:

代码语言:javascript
复制
javac -cp jackson-core-2.9.7.jar;jackson-databind-2.9.7.jar;jackson-dataformat-xml-2.9.7.jar;.
      XML2JSON.java

在运行生成的应用程序之前,您需要下载Jackson Module:JAXB Annotations,并下载StAX 2 API。我下载了JAXB Annotations版本2.9.7和StAX 2 API版本3.1.3。

假设您已成功下载jackson-module-jaxb-annotations-2.9.7.jarstax2-api-3.1.3.jar执行以下命令(分为三行以便于阅读)以运行应用程序:

代码语言:javascript
复制
java -cp jackson-annotations-2.9.7.jar;jackson-core-2.9.7.jar;jackson-databind-2.9.7.jar;
     jackson-dataformat-xml-2.9.7.jar;jackson-module-jaxb-annotations-2.9.7.jar; stax2-api-3.1.3.jar;.
     XML2JSON

如果一切顺利,您应该观察以下输出:

代码语言:javascript
复制
{"name":"Earth","planet_from_sun":3,"moons":9}

使用树遍历将XML转换为JSON

从XML转换为JSON的另一种方法是首先将XML解析为JSON节点树,然后将此树写入JSON文档。您可以通过调用其中一个XMLMapper继承的readTree()方法来完成第一个任务:

代码语言:javascript
复制
XmlMapper xmlMapper = new XmlMapper();
JsonNode node = xmlMapper.readTree(xml.getBytes());

ObjectMapperJsonNode readTree(byte[] content)方法将JSON内容反序列化为jackson.databind.JsonNode对象树,并返回JsonNode该树的根对象。在XmlMapper上下文中,此方法将XML内容反序列化为树。在任何一种情况下,JSON或XML内容都作为字节数组传递给此方法。

第二个任务 - 将对象树转换为JSON - 以与我之前显示的方式类似的方式完成。这一次,它JsonNode是传递给的根对象writeValueAsString()

代码语言:javascript
复制
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writeValueAsString(node);

我从一个XML2JSON完整源代码如清单7所示的应用程序中摘录了这些代码片段。

清单7. XML2JSON.java(版本2)

代码语言:javascript
复制
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import static java.lang.System.*;

public class XML2JSON
{
   public static void main(String[] args) throws Exception
   {
      String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
                   "<planet>\n" +
                   "    <name>Earth</name>\n" +
                   "    <planet_from_sun>3</planet_from_sun>\n" +
                   "    <moons>1</moons>\n" +
                   "</planet>\n";

      XmlMapper xmlMapper = new XmlMapper();
      JsonNode node = xmlMapper.readTree(xml.getBytes());
      ObjectMapper jsonMapper = new ObjectMapper();
      String json = jsonMapper.writeValueAsString(node);
      out.println(json);
   }
}

执行以下命令(分为两行以便于阅读)以编译清单7:

代码语言:javascript
复制
javac -cp jackson-core-2.9.7.jar;jackson-databind-2.9.7.jar;jackson-dataformat-xml-2.9.7.jar
      XML2JSON.java

在运行生成的应用程序之前,您需要下载Woodstox,它是一个实现StAX,SAX2和StAX2的高性能XML处理器。我下载了Woodstox 5.2.0。然后执行以下命令(分布在三行以便于阅读)以运行应用程序:

代码语言:javascript
复制
java -cp jackson-annotations-2.9.7.jar;jackson-core-2.9.7.jar;jackson-databind-2.9.7.jar;
     jackson-dataformat-xml-2.9.7.jar;stax2-api-3.1.3.jar;woodstox-core-5.2.0.jar;.
     XML2JSON

如果一切顺利,您应该观察以下输出:

代码语言:javascript
复制
{"name":"Earth","planet_from_sun":"3","moons":"1"}

请注意,分配给XML元素planet_from_sunmoonsXML元素的数字序列化为JSON字符串而不是数字。readTree()在没有显式类型定义的情况下,该方法不会推断数据类型。

Jackson对XML树遍历的支持还有其他限制:

  • Jackson无法区分对象和数组。由于XML无法区分对象与对象的列表(数组),因此Jackson将重复的元素整理为单个值。
  • Jackson不支持混合内容(文本内容和元素作为元素的子元素)。相反,它将每个XML元素映射到一个JsonNode对象。任何文字都会丢失。

鉴于这些限制,官方Jackson文档建议不要将XML解析为JsonNode基于树的结构也就不足为奇了。你最好使用数据绑定转换技术。

结论

本文中提供的材料应视为第二版Java XML和JSON中第6章和第11章的附录。相比之下,我的下一篇文章将与该书有关,但全新的材料。请关注我即将发布的关于使用JSON-B将Java对象绑定到JSON文档的帖子。

英文原文:https://www.javaworld.com/article/3346229/java-xml-and-json-document-processing-for-java-se-part-1.html

(未经同意,请勿转载)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 银河系1号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么选择XML和JSON?
  • Java XML和JSON,第二版
  • 第6章附录:使用XSLT转换XML文档
    • 使用SAXON超越XSLT / XPath 1.0
      • XSLT / XPath功能:一个演示
        • 清单1. XSLTDemo.java
          • XSLT 2.0示例:对节点进行分组
            • 清单2. books.xml(按书名分组)
              • 清单3. books.xsl(按作者姓名分组)
                • Transformation
                • 第11章附录:与Jackon一起处理JSON
                  • 使用Jackson将XML转换为JSON
                    • 使用数据绑定将XML转换为JSON
                      • 清单4. planet.xml
                        • 清单5. Planet.java
                          • 清单6. XML2JSON.java(版本1)
                            • 使用树遍历将XML转换为JSON
                              • 清单7. XML2JSON.java(版本2)
                              • 结论
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档