JAXP使用Stax API时格式化输出XML
最近项目中需要生成XBRL instance,对于XML读写和验证进行了一些学习。由于Stax API不支持格式化输出,默认全都写在一行上,网上也没有搜到现成的东西,自己写了一个格式化输出的帮助类。
说明:
1. 使用了WoodStox 4.2.0的Stax实现来写XML,顺便提一下,经过比较JAXP默认实现(SJSXP),WoodStox,DOM4J, XOM(NUX)。发现WoodStox速度最快,通过自己扩展的Stax2 API,功能也比较强大。
2. 实现了特殊的XML验证,一般来说使用SAXParser来读入XML文件再进行验证,但是这里使用了边写边验证的功能,如果是不合法的写入就会失败,所以只要写完XML肯定就是合法的。这也是Stax2 API提供的功能。
关于XML验证有一个相当不错的链接:http://www.edankert.com/validate.html
3. 关于XMLPrettyFormatter的用法主要在三行代码上,XbrliWriterHelper类中的 formatter.writeStartElementIndention(), formatter.writeValueIndention(), formatter.writeEndElementIndention()。注意调用这三个函数一定要writer.writerXXX()的方法前面。
以下是主要代码,最后会附上整个工程(包含XSD文件等)。
(2014-04-18 15:00 编辑) 这个实现有一个问题,会导致没有值的节点也分开显示结束标签,新的版本解决了这个问题:
http://www.cnblogs.com/lyhtbc/p/3673192.html
入口类WoodstoxWriter:
public class WoodstoxWriter { public static void main(String[] args) throws IOException, XMLStreamException { Timer t = new Timer(); t.begin(); streamWriter(); t.end(); } private static void streamWriter() throws IOException, XMLStreamException { File xsdFile = new File("src/main/resources/book.xsd"); XMLValidationSchemaFactory xsdFactory = XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA); XMLValidationSchema schema = xsdFactory.createSchema(xsdFile); XbrliWriterBean writer = new XbrliWriterBean(); writer.init(schema); writer.writeStartElement("bk", "books", Const.NSURI_BOOK); writer.writeNamespace("bk", Const.NSURI_BOOK); writer.writeNamespace("au", Const.NSURI_AUTHER); for (int i = 0; i < Const.NODE_COUNTS_S; ++i) { writer.writeStartElement("bk", "book", Const.NSURI_BOOK); writer.writeAttribute("id", String.valueOf(i + 1)); writer.writeStartElement("bk", "name", Const.NSURI_BOOK); String name = new StringBuilder("Name").append(i + i).toString(); writer.writeValue(name); writer.writeEndElement(); writer.writeStartElement("au", "author", Const.NSURI_AUTHER); writer.writeStartElement("au", "age", Const.NSURI_AUTHER); writer.writeValue("30"); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndElement(); } writer.writeEndElement(); writer.close(); } }
XbrliWriterHelper:
public class XbrliWriterHelper { private FileOutputStream out; private XMLStreamWriter2 writer; private XMLPrettyFormatter formatter; public void init(XMLValidationSchema schema) throws IOException, XMLStreamException { out = new FileOutputStream("Woodstox.xml"); XMLOutputFactory factory = XMLOutputFactory2.newInstance(); writer = (XMLStreamWriter2) factory.createXMLStreamWriter(out); writer.validateAgainst(schema); writer.writeStartDocument("UTF-8", "1.0"); formatter = new DefaultPrettyFormatter(writer); // formatter = new DefaultPrettyFormatter(writer, " "); // formatter = new EmptyPrettyFormatter(writer); } public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { formatter.writeStartElementIndention(); writer.writeStartElement(prefix, localName, namespaceURI); } public void writeValue(String value) throws XMLStreamException { formatter.writeValueIndention(); writer.writeCharacters(value.toCharArray(), 0, value.length()); } public void writeEndElement() throws XMLStreamException { formatter.writeEndElementIndention(); writer.writeEndElement(); } public void writeAttribute(String localName, String value) throws XMLStreamException { writer.writeAttribute(localName, value); } public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { writer.writeNamespace(prefix, namespaceURI); } public void close() throws XMLStreamException, IOException { writer.writeEndDocument(); writer.close(); out.close(); } }
XMLPrettyFormatter:
public interface XMLPrettyFormatter { enum NodeType { ELEMENT, VALUE } String DEFAULT_INDENTION = "\t"; void writeStartElementIndention() throws XMLStreamException; void writeValueIndention() throws XMLStreamException; void writeEndElementIndention() throws XMLStreamException; }
DefaultPrettyFormatter:
public class DefaultPrettyFormatter implements XMLPrettyFormatter { private String indent = DEFAULT_INDENTION; private int currentLevel = 0; private NodeType nodeType; private XMLStreamWriter2 writer; public DefaultPrettyFormatter(XMLStreamWriter2 writer) { this.writer = writer; } public DefaultPrettyFormatter(XMLStreamWriter2 writer, String indent) { this.writer = writer; this.indent = indent; } @Override public void writeStartElementIndention() throws XMLStreamException { nodeType = NodeType.ELEMENT; writeLineBreaker(); for (int i = 0; i < currentLevel; ++i) { writeIndention(); } ++currentLevel; } @Override public void writeValueIndention() throws XMLStreamException { nodeType = NodeType.VALUE; } @Override public void writeEndElementIndention() throws XMLStreamException { --currentLevel; if (nodeType == NodeType.ELEMENT) { writeLineBreaker(); for (int i = 0; i < currentLevel; ++i) { writeIndention(); } } else { // after write value, the node type should back to element nodeType = NodeType.ELEMENT; } } private void writeLineBreaker() throws XMLStreamException { writer.writeCharacters("\n"); } private void writeIndention() throws XMLStreamException { writer.writeCharacters(indent.toCharArray(), 0, indent.length()); } }
工程下载:晕啊,找不到上传文件地方,记得以前弄过的。。。只有直接贴文件内容了
POM 依赖
<dependencies> <dependency> <groupId>org.codehaus.woodstox</groupId> <artifactId>woodstox-core-asl</artifactId> <version>4.2.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb1-impl</artifactId> <version>2.2.5.1</version> </dependency> </dependencies>
book.xsd:
<xs:schema targetNamespace="http://www.test.com/book" xmlns="http://www.test.com/book" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:au="http://www.test.com/author" elementFormDefault="qualified"> <xs:import namespace="http://www.test.com/author" schemaLocation="author.xsd" /> <xs:element name="books" type="booksType" /> <xs:element name="book" type="bookType" /> <xs:complexType name="booksType"> <xs:sequence> <xs:element ref="book" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="name" type="xs:string" /> <xs:element ref="au:author" type="xs:string" /> </xs:sequence> <xs:attribute name="id" type="xs:string" /> </xs:complexType> </xs:schema>
author.xsd:
<xs:schema targetNamespace="http://www.test.com/author" xmlns="http://www.test.com/author" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="author" type="authorType" /> <xs:complexType name="authorType"> <xs:sequence> <xs:element name="age" type="xs:positiveInteger" /> </xs:sequence> </xs:complexType> </xs:schema>