Java 中文官方教程 2022 版(四十)(2)https://developer.aliyun.com/article/1488166
创建一个事件迭代器
第三步是创建一个事件迭代器:
XMLEventReader r = factory.createXMLEventReader (filename, new FileInputStream(filename)); while (r.hasNext()) { XMLEvent e = r.nextEvent(); System.out.println(e.toString()); }
获取事件流
最后一步是获取底层事件流:
public final static String getEventTypeString(int eventType) { switch (eventType) { case XMLEvent.START_ELEMENT: return "START_ELEMENT"; case XMLEvent.END_ELEMENT: return "END_ELEMENT"; case XMLEvent.PROCESSING_INSTRUCTION: return "PROCESSING_INSTRUCTION"; case XMLEvent.CHARACTERS: return "CHARACTERS"; case XMLEvent.COMMENT: return "COMMENT"; case XMLEvent.START_DOCUMENT: return "START_DOCUMENT"; case XMLEvent.END_DOCUMENT: return "END_DOCUMENT"; case XMLEvent.ENTITY_REFERENCE: return "ENTITY_REFERENCE"; case XMLEvent.ATTRIBUTE: return "ATTRIBUTE"; case XMLEvent.DTD: return "DTD"; case XMLEvent.CDATA: return "CDATA"; case XMLEvent.SPACE: return "SPACE"; } return "UNKNOWN_EVENT_TYPE," + eventType; }
返回输出
当你运行事件示例时,EventParse
类被编译,XML 流被解析为事件并返回到 STDOUT
。例如,Author
元素的一个实例被返回为:
<[’http://www.publishing.org’]::Author> Dhirendra Brahmachari </[’http://www.publishing.org’]::Author>
请注意,在这个示例中,事件包括一个包含命名空间的开标签和闭标签,两者都包含元素的内容作为字符串返回在标签内。
同样,一个 Cost
元素的一个实例被返回如下:
<[’http://www.publishing.org’]::Cost currency=’INR’> 11.50 </[’http://www.publishing.org’]::Cost
在这种情况下,currency
属性和值在事件的开标签中返回。
运行事件示例
- 要编译和运行事件示例,在终端窗口中,转到 INSTALL_DIR
/jaxp-
version/samples/
目录并输入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/event/*.java
- 对
BookCatalogue.xml
文件运行EventParse
示例,使用以下命令。
java stax/event/EventParse stax/data/BookCatalogue.xml
EventParse
将打印出由BookCatalogue.xml
文件定义的所有元素的数据。
过滤器示例
位于 INSTALL_DIR/jaxp-
version/samples/stax/filter/
目录中,MyStreamFilter.java
演示了如何使用 StAX 流过滤器 API 过滤应用程序不需要的事件。在这个示例中,解析器过滤掉除了 StartElement
和 EndElement
之外的所有事件。
实现 StreamFilter 类
MyStreamFilter
类实现了 javax.xml.stream.StreamFilter
:
public class MyStreamFilter implements javax.xml.stream.StreamFilter { // ... }
创建一个输入工厂
下一步是创建一个 XMLInputFactory
实例。在这种情况下,还在工厂上设置了各种属性:
XMLInputFactory xmlif = null ; try { xmlif = XMLInputFactory.newInstance(); xmlif.setProperty( XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE); xmlif.setProperty( XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); xmlif.setProperty( XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE); xmlif.setProperty( XMLInputFactory.IS_COALESCING, Boolean.TRUE); } catch (Exception ex) { ex.printStackTrace(); } System.out.println("FACTORY: " + xmlif); System.out.println("filename = "+ filename);
创建过滤器
下一步是实例化一个文件输入流并创建流过滤器:
FileInputStream fis = new FileInputStream(filename); XMLStreamReader xmlr = xmlif.createFilteredReader( xmlif.createXMLStreamReader(fis), new MyStreamFilter()); int eventType = xmlr.getEventType(); printEventType(eventType); while (xmlr.hasNext()) { eventType = xmlr.next(); printEventType(eventType); printName(xmlr,eventType); printText(xmlr); if (xmlr.isStartElement()) { printAttributes(xmlr); } printPIData(xmlr); System.out.println("-----------------------"); }
捕获事件流
下一步是捕获事件流。这与事件示例中的方式基本相同。
过滤流
最后一步是过滤流:
public boolean accept(XMLStreamReader reader) { if (!reader.isStartElement() && !reader.isEndElement()) return false; else return true; }
返回输出
当你运行过滤器示例时,MyStreamFilter
类被编译,XML 流被解析为事件并返回到 STDOUT
。例如,一个 Author
事件被返回如下:
EVENT TYPE(1):START_ELEMENT HAS NAME: Author HAS NO TEXT HAS NO ATTRIBUTES ----------------------------- EVENT TYPE(2):END_ELEMENT HAS NAME: Author HAS NO TEXT -----------------------------
同样,一个 Cost
事件被返回如下:
EVENT TYPE(1):START_ELEMENT HAS NAME: Cost HAS NO TEXT HAS ATTRIBUTES: ATTRIBUTE-PREFIX: ATTRIBUTE-NAMESP: null ATTRIBUTE-NAME: currency ATTRIBUTE-VALUE: USD ATTRIBUTE-TYPE: CDATA ----------------------------- EVENT TYPE(2):END_ELEMENT HAS NAME: Cost HAS NO TEXT -----------------------------
查看 迭代器 API 和 读取 XML 流 以获取更详细的 StAX 事件解析讨论。
运行过滤器示例
- 要编译和运行过滤器示例,在终端窗口中,转到 INSTALL_DIR
/jaxp-
version/samples/
目录并输入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/filter/*.java
- 在
java.endorsed.dirs
系统属性设置为指向samples/lib
目录的情况下,对BookCatalogue.xml
文件运行MyStreamFilter
示例,使用以下命令。
java -Djava.endorsed.dirs=../lib stax/filter/MyStreamFilter -f stax/data/BookCatalogue.xml
MyStreamFilter
将打印出由BookCatalogue.xml
文件定义的事件作为 XML 流。
读写示例
位于INSTALL_DIR/jaxp-
version/samples/stax/readnwrite/
目录中,EventProducerConsumer.java
演示了如何同时将 StAX 解析器用作生产者和消费者。
StAX XMLEventWriter
API 扩展自XMLEventConsumer
接口,并被称为事件消费者。相比之下,XMLEventReader
是一个事件生产者。StAX 支持同时读取和写入,因此可以顺序地从一个 XML 流中读取并同时写入到另一个流中。
读写示例展示了如何使用 StAX 生产者/消费者机制同时读取和写入。该示例还展示了如何修改流以及如何动态添加新事件,然后写入到不同的流中。
创建一个事件生产者/消费者
第一步是实例化一个事件工厂,然后创建一个事件生产者/消费者的实例:
XMLEventFactory m_eventFactory = XMLEventFactory.newInstance(); public EventProducerConsumer() { // ... try { EventProducerConsumer ms = new EventProducerConsumer(); XMLEventReader reader = XMLInputFactory.newInstance(). createXMLEventReader(new java.io.FileInputStream(args[0])); XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(System.out); } // ... }
创建一个迭代器
下一步是创建一个迭代器来解析流:
while (reader.hasNext()) { XMLEvent event = (XMLEvent)reader.next(); if (event.getEventType() == event.CHARACTERS) { writer.add(ms.getNewCharactersEvent(event.asCharacters())); } else { writer.add(event); } } writer.flush();
创建一个写入器
最后一步是创建一个流写入器,形式为一个新的Character
事件:
Characters getNewCharactersEvent(Characters event) { if (event.getData().equalsIgnoreCase("Name1")) { return m_eventFactory.createCharacters( Calendar.getInstance().getTime().toString()); } // else return the same event else { return event; } }
返回输出
运行读写示例时,EventProducerConsumer
类被编译,并且 XML 流被解析为事件并写回到STDOUT
。输出是示例 XML 文档中描述的BookCatalog.xml
文件的内容。
运行读写示例
- 要编译和运行读写示例,在终端窗口中,转到INSTALL_DIR
/jaxp-
version/samples/
目录并输入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/readnwrite/*.java
- 在
BookCatalogue.xml
文件上运行EventProducerConsumer
示例,使用以下命令。
java stax/readnwrite/EventProducerConsumer stax/data/BookCatalogue.xml
EventProducerConsumer
将打印出BookCatalogue.xml
文件的内容。
写入器示例
位于INSTALL_DIR/jaxp-
version/samples/stax/writer/
目录中,CursorWriter.java
演示了如何使用 StAX 游标 API 编写 XML 流。
创建输出工厂
第一步是创建一个XMLOutputFactory
的实例:
XMLOutputFactory xof = XMLOutputFactory.newInstance();
创建一个流写入器
下一步是创建一个XMLStreamWriter
的实例:
XMLStreamWriter xtw = null;
写入流
最后一步是写入 XML 流。请注意,在写入最终的EndDocument
后,流会被刷新并关闭:
xtw = xof.createXMLStreamWriter(new FileWriter(fileName)); xtw.writeComment("all elements here are explicitly in the HTML namespace"); xtw.writeStartDocument("utf-8","1.0"); xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40"); xtw.writeStartElement("http://www.w3.org/TR/REC-html40","html"); xtw.writeNamespace("html", "http://www.w3.org/TR/REC-html40"); xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "head"); xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "title"); xtw.writeCharacters("Frobnostication"); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "body"); xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "p"); xtw.writeCharacters("Moved to"); xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "a"); xtw.writeAttribute("href","http://frob.com"); xtw.writeCharacters("here"); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeEndElement(); xtw.writeEndDocument(); xtw.flush(); xtw.close();
返回输出
运行写入器示例时,CursorWriter
类被编译,并且 XML 流被解析为事件并写入到名为dist/CursorWriter-Output
的文件中:
<!--all elements here are explicitly in the HTML namespace--> <?xml version="1.0" encoding="utf-8"?> <html:html > <html:head> <html:title>Frobnostication</html:title></html:head> <html:body> <html:p>Moved to <html:a href="http://frob.com">here</html:a> </html:p> </html:body> </html:html>
在实际的dist/CursorWriter-Output
文件中,该流是连续写入的,没有任何换行符;这里添加了换行符以便更容易阅读清单。在这个示例中,与事件示例中的对象流一样,命名空间前缀被添加到 HTML 标签的开头和结尾。虽然 StAX 规范不要求添加这个前缀,但是当输出流的最终范围不明确时,这是一个良好的实践。
运行写入器示例
- 要编译和运行 Writer 示例,在终端窗口中,转到 INSTALL_DIR
/jaxp-
version/samples/
目录,并输入以下内容:
javac -classpath \ ../lib/jaxp-ri.jar stax/writer/*.java
- 运行
CursorWriter
示例,指定输出应写入的文件名。
java stax/writer/CursorWriter -f *output_file*
CursorWriter
将创建一个包含 返回输出 中显示的数据的相应名称的输出文件。
更多信息
有关 StAX 的更多信息,请参见:
- Java 社区进程页面.
- W3C 推荐 可扩展标记语言(XML)1.0
- XML 信息集
- jcp.org 上的 JAXB 规范:JSR-222 Java XML 绑定架构(JAXB)
- W3C 推荐 文档对象模型
- SAX 简单 XML API
- DOM 文档对象模型
- W3C 推荐 XML 中的命名空间
有关使用 StAX 的一些有用文章,请参见:
- Jeff Ryan, StAX 是否应该成为您的 XML 工具箱中的一部分?
- Elliott Rusty Harold, StAX 简介
课程:JAXP 1.5 和新属性
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/index.html
本课程重点介绍了 JAXP 1.5 中引入的新属性。
JAXP 1.5 被添加到了 7u40 和 JDK 8 版本中。你可以从java.net
下载当前的JDK 8 快照。
背景
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/backgnd.html
JAXP 安全处理功能对 XML 处理器施加资源限制,以抵御某些类型的拒绝服务攻击。 但是,它并不限制获取外部资源的方式,这在尝试安全处理 XML 文档时也是有用的。 当前的 JAXP 实现支持特定于实现的属性,可用于强制执行此类限制,但需要一种标准方法来实现。
JAXP 1.5 添加了三个新属性以及它们对应的系统属性,允许用户指定可以或不可以允许的外部连接类型。属性值是协议列表。 JAXP 处理器通过将协议与列表中的协议进行匹配来检查给定的外部连接是否被允许。 如果连接在列表中,则处理器将尝试建立连接,否则将拒绝连接。
JAXP 1.5 已经集成到 7u40 和 JDK8 中。
外部资源
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/resources.html
XML、Schema 和 XSLT 标准支持以下需要外部资源的构造。JDK XML 处理器的默认行为是建立连接并按照指定的方式获取外部资源。
- 外部 DTD:引用外部文档类型定义(DTD),示例:
- 外部实体引用:引用外部数据,语法:
通用实体引用如下:
<?xml version="1.0" standalone="no" ?> <!DOCTYPE doc [<!ENTITY otherFile SYSTEM "otherFile.xml">]> <doc> <foo> <bar>&otherFile;</bar> </foo> </doc>
- 外部参数实体,语法
。例如:
<?xml version="1.0" standalone="no"?> <!DOCTYPE doc [ <!ENTITY % foo SYSTEM "http://www.example.com/student.dtd"< %foo; ]>
- XInclude:在 XML 文档中包含外部信息集
- 使用
schemaLocation
属性、import
和include
元素引用 XML Schema 组件。示例:schemaLocation="http://www.example.com/schema/bar.xsd"
- 使用
import
或include
元素合并样式表:语法: - xml-stylesheet 处理指令:用于在 xml 文档中包含样式表,语法:
- XSLT
document()
函数:用于访问外部 XML 文档中的节点。例如,新属性
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/properties.html
JAXP 1.5 定义了三个新属性,用于调节 XML 处理器是否解析上述外部资源。这些属性是:
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD
javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA
javax.xml.XMLConstants.ACCESS_EXTERNAL_STYLESHEET
这些 API 属性具有相应的系统属性和 jaxp.properties。
ACCESS_EXTERNAL_DTD
名称:
http://javax.xml.XMLConstants/property/accessExternalDTD
定义:限制对外部 DTD、外部实体引用到指定协议的访问。
值:参见属性的值
默认值:
all
,允许连接到所有协议。系统属性:
javax.xml.accessExternalDTD
ACCESS_EXTERNAL_SCHEMA
名称:
http://javax.xml.XMLConstants/property/accessExternalSchema
定义:限制对由
schemaLocation
属性、Import 和 Include 元素设置的外部引用协议的访问。值:参见属性的值
默认值:
all
,允许连接到所有协议。系统属性:
javax.xml.accessExternalSchema
ACCESS_EXTERNAL_STYLESHEET
名称:
http://javax.xml.XMLConstants/property/accessExternalStylesheet
定义:限制对由样式表处理指令、文档函数、Import 和 Include 元素设置的外部引用协议的访问。
值:参见属性的值
默认值:
all
,允许连接到所有协议。系统属性:
javax.xml.accessExternalStylesheet
${java.home}/lib/jaxp.properties
这些属性可以在
jaxp.properties
中指定,以定义所有使用 Java Runtime 的应用程序的行为。格式为property-name=[value][,value]*
。例如:javax.xml.accessExternalDTD=file,http
属性名称与系统属性相同:
javax.xml.accessExternalDTD
、javax.xml.accessExternalSchema
和javax.xml.accessExternalStylesheet
。属性的值
所有属性的值格式相同。
值:由逗号分隔的协议列表。协议是 URI 的 scheme 部分,或者在 JAR 协议的情况下,由冒号分隔的"jar"加上 scheme 部分。协议定义为:
scheme = alpha *( alpha | digit | "+" | "-" | "." )
其中 alpha = a-z 和 A-Z。
以及 JAR 协议:
jar[:scheme]
协议不区分大小写。值中由
Character.isSpaceChar
定义的任何空格将被忽略。协议的示例包括file
、http
、jar:file
。默认值:默认值是实现特定的。在 JAXP 1.5 RI、Java SE 7u40 和 Java SE 8 中,默认值为
all
,授予所有协议的权限。授予所有访问:关键字
all
授予所有协议的权限。例如,在jaxp.properties
中设置javax.xml.accessExternalDTD=all
将允许系统像以前一样工作,无限制地访问外部 DTD 和实体引用。拒绝任何访问:空字符串,即"",表示不授予任何协议权限。例如,在
jaxp.properties
中设置javax.xml.accessExternalDTD=""
将指示 JAXP 处理器拒绝任何外部连接。范围和顺序
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/scope.html
javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING
(FSP)是包括 DOM、SAX、Schema Validation、XSLT 和 XPath 在内的 XML 处理器的必需功能。当设置为true
时,建议实现启用由上述新属性定义的访问限制。为了兼容性,尽管对于 DOM、SAX 和 Schema Validation,默认情况下 FSP 为 true,但 JAXP 1.5 不会启用新的限制。对于 JDK 8,建议将新的
accessExternal*
属性在 FSP 被明确设置时设置为空字符串。这仅在通过 API 设置 FSP 时才会发生,例如factory.setFeature(FSP, true)
。尽管对于 DOM、SAX 和 Schema Validation,默认情况下 FSP 为 true,但 JDK 8 并不将其视为“明确”设置,因此默认情况下不会设置限制。在
jaxp.properties
文件中指定的属性会影响 JDK 或 JRE 的所有调用,并将覆盖其默认值,或者可能已经由 FEATURE_SECURE_PROCESSING 设置的值。当设置系统属性时,将仅影响一个调用,并将覆盖默认设置或在 jaxp.properties 中设置的设置,或者可能已经由 FEATURE_SECURE_PROCESSING 设置的设置。
通过 JAXP 工厂或
SAXParser
指定的 JAXP 属性优先于系统属性,jaxp.properties
文件以及javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING
。新的 JAXP 属性在以下情况下对其试图限制的相关构造没有影响:
- 当存在解析器并且解析器返回的源不为 null 时。这适用于可能设置在 SAX 和 DOM 解析器上的实体解析器,StAX 解析器上的 XML 解析器,SchemaFactory 上的 LSResourceResolver,验证器或 ValidatorHandler,或者转换器上的 URIResolver。
- 当通过调用
SchemaFactory
的newSchema
方法显式创建模式时。 - 当不需要外部资源时。例如,以下功能/属性由参考实现支持,并可用于指示处理器不加载外部 DTD 或解析外部实体。
http://apache.org/xml/features/disallow-doctype-decl true http://apache.org/xml/features/nonvalidating/load-external-dtd false http://xml.org/sax/features/external-general-entities false http://xml.org/sax/features/external-parameter-entities false
与安全管理器的关系
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/security.html
在尝试连接之前,将首先检查 JAXP 属性,无论是否存在
SecurityManager
。这意味着即使SecurityManager
授予权限,连接也可能被阻止。例如,如果 JAXP 属性被设置为禁止 http 协议,它们将有效地阻止任何连接尝试,即使应用程序具有SocketPermission
。为了限制连接,
SecurityManager
可以被视为处于较低级别。在评估 JAXP 属性之后,权限将被检查。例如,如果一个应用程序没有SocketPermission
,即使 JAXP 属性被设置为允许 http 连接,也会抛出SecurityException
。当存在
SecurityManager
时,JAXP FEATURE_SECURE_PROCESSING
被设置为 true。这种行为不会启用新的限制。JDK 中的属性设置
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/propSettings.html
以下表格显示了 JDK 中新属性的默认值和行为。
访问属性的值 默认值 设置 FSP(a) jaxp.properties 系统属性 API 属性 7u40 all
无更改 覆盖 覆盖 覆盖 JDK8 all
更改为 “” 覆盖 覆盖 覆盖 (a) 设置 FSP 意味着明确使用 JAXP 工厂的
setFeature
方法设置 FEATURE_SECURE_PROCESSING。(b) 7u40 和 JDK8 之间唯一的行为差异是,在 7u40 中设置 FSP 不会更改
accessExternal*
属性,但在 JDK8 中会将值设置为空字符串。在 JDK8 中,设置 FSP 被视为选择加入。© 表中从左到右的顺序反映了覆盖顺序。例如,如果通过 API 设置了
accessExternal
属性,则会覆盖其他可能已通过其他方式设置的属性。Java 中文官方教程 2022 版(四十)(4)https://developer.aliyun.com/article/1488170