saxon 使用
XML和JSON对我来说很重要,我感谢Apress让我写了一整本有关它们的书。 在本文中,我将简要介绍新书的第二版Java XML和JSON 。 如果有足够的空间,我还将展示两个有用的演示,希望将它们包含在书中。
首先,我将向您展示如何使用XSLT 2.0+和XPath 2.0+兼容的替代品(在本例中为SAXON)重写Xalan,这是Java 11的标准XSLT实现。 使用SAXON for XSLT / XPath可以更轻松地访问诸如分组之类的功能,我还将对此进行演示。 接下来,我将向您展示两种使用Jackson转换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的第二版分为三个部分,包括12章和附录:
第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,该API也可以非正式地用于Java SE上下文中。
每章都以一组练习(包括编程练习)结尾,这些练习旨在增强读者对材料的理解。 答案在本书的附录中显示。
新版本在某些方面与先前版本有所不同:
此版本还纠正了上一版本内容中的细微错误,更新了各种数字,并增加了许多新的练习。
虽然在第二版中没有足够的空间,但将来的Java XML和JSON版本可能会涵盖YAML 。
XML和JSON通常会进行比较,但是它们实际上是相辅相成的。 卢卡斯·沃格尔(Lucas Vogel)在他的文章中指出了这一点: “ JSON与XML:争夺格式霸权之争可能是浪费精力 。”
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:
System.setProperty("javax.xml.transform.TransformerFactory",
"net.sf.saxon.TransformerFactoryImpl");
实际上,您不需要此方法调用,因为SAXON的TransformerFactory
实现作为一种服务提供在JAR文件中,当可通过类路径访问JAR文件时,该服务会自动加载。 但是,如果类路径上有多个TransformerFactory
实现JAR文件,并且Java运行时选择了非SAXON服务作为转换器实现,则可能会出现问题。 包括上述方法调用将使用SAXON覆盖该选择。
第6章介绍了两个XSLTDemo
应用程序,书的代码档案中提供了第三个应用程序。 下面的清单1展示了第四个XSLTDemo
演示应用程序,该应用程序重点介绍了XSLT / XPath功能。
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,但是有一些区别。 首先,必须使用两个命令行参数调用清单1的main()
方法:第一个参数命名XML文件;第二个参数命名XML文件。 第二个参数命名XSL文件。
第二个区别是我没有在变压器上设置任何输出属性。 具体来说,我没有指定输出方法或是否使用缩进。 这些任务可以在XSL文件中完成。
如下编译清单1:
javac XSLTDemo.java
XSLT 1.0不提供对分组节点的内置支持。 例如,您可能想要转换以下XML文档,该文档列出了作者的书籍:
<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中,该XML列出了作者及其书籍:
<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中可以进行这种转换,但是很尴尬。 相比之下,XSLT 2.0的xsl:for-each-group
元素使您可以获取一组节点,按某个条件对其进行分组,并处理每个创建的组。
让我们从处理XML文档开始探讨这种功能。 清单2给出了books.xml
文件的内容,该文件按书名对作者姓名进行分组。
<?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转换,可以将该文档转换为根据作者姓名对书名进行分组的文档。
<?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
元素选择一个节点序列并将其组织为groups 。 select
属性是一个XPath表达式,用于标识要分组的元素。 在这里,告诉您选择属于book
元素的所有author
元素。 group-by
属性将所有具有相同键值的元素归为一组 ,恰好是author
元素的@name
属性。 从本质上讲,您最终分为以下几组:
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"
构造将xsl:sort select="../@title"
对输出book
元素进行排序,该元素通过随后的<book title="{../@title}" />
结构指定。
saxon 使用