jaxp对xml的操作(JAXP的演进)

最近重新温习了Jaxp对xml的操作,在网上搜索了一些这方面的资料!觉得非常好,在此mark一下。感觉Sun的东西才是王道!英文版的资料更加好!


JAXP的演进

作者:Rahul Srivastava

Rahul Srivastava;SJTUer
原文地址:http://www.xml.com/pub/a/2005/07/06/jaxp.html
中文地址:http://www.matrix.org.cn/resource/article/43/43893_JAXP.html
关键词: JAXP XML


简介
在1998年W3CXML1.0推荐标准发布之后,XML就开始变得很流行。Sun公司就是在那时候规范Java Community Process (JCP),同时JAXP(JSR-05)的第一版在2000早些时候发布了。这个版本得到了很多工业集团的支持,譬如(以年月次序排列)BEA Systems, Fujitsu Limited, Hewlett-Packard, IBM, Netscape Communications, Oracle, and Sun Microsystems, Inc.

JAXP (全称Java API for XML Parsing)的可插拔性(pluggability)在开发社区里引起很大的轰动。这点也是JAXP的精华所在。开发人员可以编写自己的xml处理器,只要它符合JAXP的APIs,这样底层不同的xml处理器可以任意切换而不用改应用程序的代码。

那JAXP到底是什么呢?首先 这个P有点迷惑,它代表Parsing还是Processing呢?
因为JAXP1.0的时候只支持解析(parsing),所以JAXP全称应该是Java API for XML Parsing.
但在JAXP1.1的时候,XSL-T被推荐用作XML的转换(transformation)处理。很遗憾,当时W3C XLT-T的标准规范(specification)里没有提供任何用来转换(transformation)处理的APIs。因此JAXP1.1的专家组推荐了一组APIs叫Transformation API for XML (TrAX)。
从此JAXP就叫Java API for XML Processing. JAXP通过逐步进化,支持的东西也越来越多
不仅仅是解析xml文件(譬如在解析文档的时候根据schema校验有效性,根据预解析的schema来校验文档有效性,计算XPath 表达式等等)。

由于底层用来处理xml文档的可插拔的processor是任意编写的,只要它符合JAXP的规范,因此JAXP 是一个轻量级的处理xml文件的处理APIs。(译者注:JAXP只是一个api规范而已,真正底层实现是任意的。后面会有具体介绍。)

使用JAXP来解析XML文档
JAXP支持基于对象和基于事件的两种解析方式。基于对象的解析,到目前为止只支持W3C DOM解析,JAXP的专家组可能在JAXP的将来版本中会支持J-DOM规范。基于事件的解析,只有SAX 解析模式被支持,另一个基于事件的解析模式叫Pull Parsing,本来它应该是JAXP的一部分。但是对于Pull Parsing存在有一份不同的JSR (#173)文档,也就是大家所知道的Streaming API for XML (StAX) parsing,现在我们对于那个也没什么更多的可以做了。

image
Figure 1: Various mechanism of parsing XML

使用SAX来解析XML文档
SAX APIs 是在1998年的早些时候由David Megginson提出的,目标是成为基于事件驱动的xml文档解析模式的标准API(这里你可以的到一些 SAX 的历史信息)。即使这样,SAX仍不是W3C 的REC。但毫无疑问实际中它是行业内解析XML文档的标准。

SAX 是一种基于事件的解析模式,是push-parsing原理,解析文档的时候,当遇到<opening> 标签, </closing>标签 或字符等,SAX 都会产生相应的事件(event)。一个SAX解析器解析XML文档的时候,把文档看作为一个流,依次产生相应的事件报告给已注册的content handler, org.xml.sax.ContentHandler,如果有错误,错误会报告给error handler, org.xml.sax.ErrorHandler.

如果你不注册一个error handler,那你就根本不会知道在解析XML文档的时候有没有错误产生和错误是什么。因此,在SAX解析XML文档的时候注册一个error handler是极其重要的。

如果程序需要知道有什么事件产生了(并且想处理此事件),那你必须实现org.xml.sax.ContentHandler 接口并注册给 SAX解析器。一个典型的事件被触发的顺序是
startDocument, startElement, characters, endElement, endDocument。
startDocument 仅仅被触发一次而且是在触发其它event之前。同样,endDocument仅仅被触发一次而且是在整个文档被成功解析之后。你可以从SAX javadocs中获取更详细的信息。

image
Figure 2: SAX Parsing XML

使用JAXP,通过SAX parse XML document的代码片断:
1 SAXParserFactory spfactory = SAXParserFactory.newInstance();
2 spfactory.setNamespaceAware(true);
3 SAXParser saxparser = spfactory.newSAXParser();
4
5 //write your handler for processing events and handling error
6 DefaultHandler handler = new MyHandler();
7
8 //parse the XML and report events and errors (if any) to the handler
9 saxparser.parse(new File("data.xml"), handler);

文档对象模型解析
DOM 解析是基于对象的原理,当用DOM解析XML文档时它会在内存中生成一个树形的结构来表示一个XML文档。树上的每个节点代表着XML文档中的一个节点。如果一个DOM解析器符合W3C标准,那它产生的DOM就是W3C的DOM,使用org.w3c.dom APIs就能遍历和修改这个DOM。

大部分DOM解析器允许你抽取XML文档里的一部分来生成DOM树,而不是把整个XML文档在内存中建立对应DOM树。

image
Figure 3: DOM Parsing XML


使用JAXP, 通过DOM parse XML document的代码片断:
1 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
2 dbfactory.setNamespaceAware(true);
3 DocumentBuilder domparser = dbfactory.newDocumentBuilder();
4
5 //parse the XML and create the DOM
6 Document doc = domparser.parse(new File("data.xml"));
7
8 //to create a new DOM from scratch -
9 //Document doc = domparser.newDocument();
10
11 //once you have the Document handle, then you can use
12 //the org.w3c.dom.* APIs to traverse or modify the DOM

在校验模式下进行解析
根据DTD校验
DTD 是XML 文档的语法。经常人们会觉得DTD有点另类,因为它和XML的syntax不一样,但DTD是W3C XML1.0里的完整的一部分。如果一份XML文档声明了DOCTYPE,并且想在解析的时候根据DTD校验文档,那你必须在适当的factory里启用根据DTD校验文档(validation)这个特性。例如:
1 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
2 dbfactory.setValidating(true);
3
4 OR
5
6 SAXParserFactory spfactory = SAXParserFactory.newInstance();
7 spfactory.setValidating(true);
8

注意,如果XML文档声明了一个DTD ,即使你不启用校验(validation)这个特性,解析器总是试着去读入这个DTD。 这样做的目的是为了保证XML文档中entity reference被正确的扩展了,否则会导致格式不正确的XML文档,只有在XML文档序言部分的声明中standalone属性被置为true时,外部的DTD才会被完全忽略掉。例如:
1<?xml version="1.1" encoding="UTF-8" standalone="yes"?>

根据W3C Schema来校验XML文档(WXS)
XMLSchema 是XML文档的另外一种文法描述。XMLSchema非常流行市因为它和XML文档使用同样的语法并且提供了丰富的定义校验限制的特性。如果一个XML文档用"schemaLocation" 和"noNamespaceSchemaLocation"指向了一个schema,结下来你想启用根据XMLSchema校验文档这个特性,你还要做如下的步骤:
1.和上面说的一样,调用SAXParserFactory o或DocumentBuilderFactory的setValidating函数来启用validation这个特性。
2.把属性 "http://java.sun.com/xml/jaxp/properties/schemaLanguage" 值设为 "http://www.w3.org/2001/XMLSchema"

注意,这种情况下,即使XML文档有DOCTYPE声明,处理器仍不会用DTD来校验这个文档。但是和前面提到的一样,为了任何一个entity reference是被正确扩展的,这个DTD还是会被装载的,
既然"schemaLocation" 和"noNamespaceSchemaLocation"仅仅是提示,所以可以使用属性"http://java.sun.com/xml/jaxp/properties/schemaSource"从外部提供schemas来覆盖这些提示。
对于这个属性,一些可以接受值是:

·是一个代表schema的URL地址的字符串。
·java.io.InputStream with the contents of the schema
·org.xml.sax.InputSource
·java.io.File
·一个 java.lang.Object 的数组,数组内容是上面所提到三类中的一个。
例如:
1 SAXParserFactory spfactory = SAXParserFactory.newInstance();
2 spfactory.setNamespaceAware(true);
3
4 //turn the validation on
5 spfactory.setValidating(true);
6
7 //set the validation to be against WXS
8 saxparser.setProperty("http://java.sun.com/xml/jaxp/properties/
9 schemaLanguage", "http://www.w3.org/2001/XMLSchema");
10
11 //set the schema against which the validation is to be done
12 saxparser.setProperty("http://java.sun.com/xml/jaxp/properties/
13 schemaSource", new File("myschema.xsd"));

使用JAXP的TrAX APIs来进行XML文档转换处理工作
W3C XSL-T 定义了一些转换规则来把源树转化生成结果树。在XSL-T中,转换信息所存在的文件叫样式表(stylesheet)。要用JAXP来转换一个XML文档,你需要定义一个使用样式表来转换XML文档的转换器。创建好这样的转换器后,它把要转换的XML文档作为JAXP的source,返回转换好的结果作为JAXP的result。目前JAXP提供三种类型的source和result:
StreamSource, SAXSource, DOMSource and StreamResult, SAXResult, DOMResult, 他们是能够联合使用的。

image
Figure4: XML Transformation

从DOM中生成SAX Events:
1 //parse the XML file to a W3C DOM
2 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
3 dbfactory.setNamespaceAware(true);
4
5 DocumentBuilder domparser = dbfactory.newDocumentBuilder();
6 Document doc = domparser.parse(new File("data.xml"));
7
8 //prepare the DOM source
9 Source xmlsource = new DOMSource(doc);
10
11 //create a content handler to handle the SAX events
12 ContentHandler handler = new MyHandler();
13
14 //prepare a SAX result using the content handler
15 Result result = new SAXResult(handler);
16
17 //create a transformer factory
18 TransformerFactory xfactory = TransformerFactory.newInstance();
19
20 //create a transformer
21 Transformer xformer = xfactory.newTransformer();
22
23 //transform to raise the SAX events from DOM
24 xformer.transform(xmlsource, result);

上面的例子中,我们创建Transformer的时候没有用到XSL。这意味着这个转换器对XML不会有任何的转换,source和result是一样的。当你实际相要用XSL来转换,你应该创建一个使用了XSL的转换器,就像下面一样:
1 //create the xsl source
2 Source xslsource = new StreamSource(new File("mystyle.xsl"));
3
4 //create the transformer using the xsl source
5 Transformer xformer = xfactory.newTransformer(xslsource);

JAXP1.3有哪些新特性?
除了支持SAX解析,DOM解析,根据DTD/ XMLSchema的校验,使用XSL-T转换,
和以前的版本相比JAXP1.3新支持的特性有:
1. XML 1.1 和XML 1.1名字空间
2. XML Inclusions - XInclude 1.0
3. 根据预解析的schema来校验文档。
4. XPath表达式的计算.
5. 以前XMLSchema 1.0, XPath 2.0 和XQuery 1.0中的某些数据类型不能被映射到java里的数据类型 ,现在可以了。

使用JAXP1.3
XML1.1主要支持的特性如下:
1.向前兼容不断增长的Unicode字符集。
2.在行结束(line-end)字符集中新添加了NEL (#x85)和Unicode行分隔符(#x2028)。

XML1.1中的变更不是向下兼容的,XML1.0中的一些well-formedness规则在
XML1.1中可能就不适用。所以XML1.1的规范是全新的而不是从XML1.0规范上升级。
为了能够使用XML1.1和XML1.1的名字空间,你必须在XML序言声明中把version属性的值设为“1.1”。例如:
1<?xml version="1.1" encoding="UTF-8" standalone="yes"?>

XInclude允许一个XML文档包含另一个XML文档,例如:
1 <myMainXMLDoc xmlns:xi="http://www.w3.org/2001/XInclude">
2 <xi:include href="fragment.xml"/>
3
4 </myMainXMLDoc>

相要使用XML inclusions特性,你必须在适当的factory里设置XInclude属性为true,就像下面代码所示:
1 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
2 dbfactory.setXIncludeAware(true);

根据预解析的schema校验JAXP的输入源

javax.xml.validation包提供了解析schema和根据预解析的schema校验XML文档的功能。DOMSource和SAXSource是可以根据预解析的schema来被校验。如果需要可以缓存预解析的schema来达到优化的目的。必须注意到的是,根据预解析的schema校验JAXP的输入源中,StreamSource并不是被支持的源,还有schema可以是W3C XML Schema 或者是一个OASIS RELAX-NG。例如:
1 //parse an XML in non-validating mode and create a DOMSource
2 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
3 dbfactory.setNamespaceAware(true);
4 dbfactory.setXIncludeAware(true);
5
6 DocumentBuilder parser = dbfactory.newDocumentBuilder();
7 Document doc = parser.parse(new File("data.xml"));
8
9 DOMSource xmlsource = new DOMSource(doc);
10
11 //create a SchemaFactory for loading W3C XML Schemas
12 SchemaFactory wxsfactory =
13 SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
14
15 //set the errorhandler for handling errors in schema itself
16 wxsfactory.setErrorHandler(schemaErrorHandler);
17
18 //load a W3C XML Schema
19 Schema schema = wxsfactory.newSchema(new File("myschema.xsd"));
20
21 // create a validator from the loaded schema
22 Validator validator = schema.newValidator();
23
24 //set the errorhandler for handling validation errors
25 validator.setErrorHandler(validationErrorHandler);
26
27 //validate the XML instance
28 validator.validate(xmlsource);

计算XPath表达式

javax.xml.xpath 包提供了根据XML文档计算XPath表达式的功能。如果一个表达式要被重用,出于性能考虑,这个XPath表达式会被编译。
顺便说一下,JAXP中的XPath 的API被设计为无状态的,这就意味着每次你要计算一个XPath表达式,你都要传入一个XML的文档。通常,很多XPath表达式是根据单个XML文档来计算的。这种情况下,如果JAXP中的XPath APIs是有状态的,XML文档只需传入一次,那样就更好了。对于底层实现来说就多了一个优化选择,可以把XML 文档源存储起来,这样就可以快速计算XPath表达式了。
一个根据XML文档计算XPath表达式得例子:
1 <?xml version="1.0"?>
2 <employees>
3 <employee>
4 <name>e1</name>
5 </employee>
6 <employee>
7 <name>e2</name>
8 </employee>
9 </employees>

1 //parse an XML to get a DOM to query
2 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
3 dbfactory.setNamespaceAware(true);
4 dbfactory.setXIncludeAware(true);
5
6 DocumentBuilder parser = dbfactory.newDocumentBuilder();
7 Document doc = parser.parse(new File("data.xml"));
8
9 //get an XPath processor
10 XPathFactory xpfactory = XPathFactory.newInstance();
11 XPath xpathprocessor = xpfactory.newXPath();
12
13 //set the namespace context for resolving prefixes of the Qnames
14 //to NS URI, if the xpath expresion uses Qnames. XPath expression
15 //would use Qnames if the XML document uses namespaces.
16 //xpathprocessor.setNamespaceContext(NamespaceContext nsContext);
17
18 //create XPath expressions
19 String xpath1 = "/employees/employee";
20 XPathExpression employeesXPath = xpathprocessor.compile(xpath1);
21
22 String xpath2 = "/employees/employee[1]";
23 XPathExpression employeeXPath = xpathprocessor.compile(xpath2);
24
25 String xpath3 = "/employees/employee[1]/name";
26 XPathExpression empnameXPath = xpathprocessor.compile(xpath3);
27
28 //execute the XPath expressions
29 System.out.println("XPath1="+xpath1);
30 NodeList employees = (NodeList)employeesXPath.evaluate(doc,
31 XPathConstants.NODESET);
32 for (int i=0; i<employees.getLength(); i++) {
33 System.out.println(employees.item(i).getTextContent());
34 }
35
36 System.out.println("XPath2="+xpath2);
37 Node employee = (Node)employeeXPath.evaluate(doc, XPathConstants.NODE);
38 System.out.println(employee.getTextContent());
39
40 System.out.println("XPath3="+xpath3);
41 String empname = empnameXPath.evaluate(doc);
42 System.out.println(empname);

XML和java数据类型间的映射
Datatypes 在XMLSchema1.0的时候就很流行了,被很多其他XML规范所应用,像 XPath,XQuery,WSDL等。大部分的数据类型可以映射到java的基本数据类型或包装过的数据类型。其他的类型如:dataTime,duration可以被映射到新的java数据类型: javax.xml.datatype.XMLGregorianCalendar, javax.xml.datatype.Duration, and javax.xml.namespace.QName. 这样XMLSchema1.0 XPath 2.0 和XQuery 1.0中所有数据类型,JAVA中都有对应的类型存在。

从可用性角度看,如果DatatypeFactory有方法能够生成一个对应WXS中数据类型的java对象,并且这个java对象拥有方法能根据facets限制数据类型和根据值来校验数据类型,这样就非常好了。
一个使用Oracle's XDK的例子:
1 import oracle.xml.parser.schema.*;
2 . . .
3
4 //create a simpleType object
5 XSDSimpleType st = XSDSimpleType.getPrimitiveType(XSDSimpleType.iSTRING);
6
7 //set a constraining facet on the simpleType
8 st.setFacet(XSDSimpleType.LENGTH, "5");
9
10 //validate value
11 st.validateValue("hello");

底层实现间的切换
一个JAXP的实现通常包括一个默认的解析器,转换器,xpath引擎和schema校验器,
但是就像文章开始的时候所提到的那样,JAXP是一个可插拔的API,我们可以插入我们自己的处理器来替换JAXP默认的处理器。要实现这样的切换,我们可以通过设置属性javax.xml.xxx.yyyFactory的值来指定一个合格的factory的实现类。当yyyFactory.newInstance()被调用的时候,JAXP使用如下的顺序查找需要装载的具体的实现类:

1.使用javax.xml.xxx.yyyFactory属性指定的值.
2.使用 JRE 目录下lib/jaxp.properties 属性文件。Jaxp.properties文件只被读入一次,它的值会被缓存已被将来所用。如果第一次尝试去读这个文件而此文件不存在的话,以后就不会尝试着去检查此文件是否存在。jaxp.properties里面的值第一次读过之后,就不可能被修改了。
3.如果可以地话,可以使用Services API(在JAR的规范里有详细的信息)来决定哪个实现类被载入。Services API会在runtime时存在的jars的META-INF/services/javax.xml.xxx.yyyFactory里寻找那个classname。
4.使用平台默认的javax.xml.xxx.yyyFactory 实例。
javax.xml.xxx.yyyFactory 可是下面其中的一个:
javax.xml.parsers.SAXParserFactory
javax.xml.parsers.DocumentBuilderFactory
javax.xml.transform.TransformerFactory
javax.xml.xpath.XPathFactory
javax.xml.validation.SchemaFactory:schemaLanguage (schemaLanguage 是调用SchemaFactory的newInstance函数时所提供的参数)

例如:想在JAXP中使用SAX解析器,你可以用上面提到的4个方法中的任何一个把
javax.xml.parsers.SAXParserFactory设为org.apache.xerces.jaxp.SAXParserFactoryImpl。
其中的一个方法如下:
1 java -Djavax.xml.parsers.SAXParserFactory= org.apache.xerces.jaxp.SAXParserFactoryImpl MyApplicationProgram
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 可以使用 Java API for XML Processing (JAXP) 中的 Validation API 对 XML 文件进行格式验证。具体实现步骤如下: 1. 创建一个 SAXParserFactory 实例,用于创建 SAXParser。 2. 设置 SAXParserFactory 的属性,使其支持 XML Schema 验证。 3. 创建一个 SAXParser 实例。 4. 实现一个 DefaultHandler,用于处理 SAX 事件。 5. 调用 SAXParser.parse() 方法,将 XML 文件解析为 SAX 事件流,并将 DefaultHandler 作为解析器的回调函数。 6. 如果 XML 文件格式不符合指定的 XML Schema,则会抛出 SAXException 异常。 下面是一个简单的示例代码,用于验证一个 XML 文件是否符合指定的 XML Schema: ```java import javax.xml.XMLConstants; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class XmlValidator extends DefaultHandler { private boolean isValid = true; public boolean validate(String xmlFilePath, String xsdFilePath) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(true); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = schemaFactory.newSchema(new File(xsdFilePath)); factory.setSchema(schema); SAXParser parser = factory.newSAXParser(); parser.parse(new File(xmlFilePath), this); } catch (Exception e) { isValid = false; } return isValid; } @Override public void error(SAXParseException e) throws SAXException { isValid = false; } @Override public void fatalError(SAXParseException e) throws SAXException { isValid = false; } } ``` 在上述代码中,validate() 方法接受两个参数,分别是要验证的 XML 文件路径和 XML Schema 文件路径。如果 XML 文件符合指定的 XML Schema,将返回 true,否则返回 false。如果 XML 文件格式不符合指定的 XML Schema,将抛出 SAXException 异常,isValid 属性将被设置为 false。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值