我们将如何使用Java处理各种XML文档?
我正在寻找使用Java应用程序解析各种XML文档的最佳方法。我目前正在使用SAX和自定义内容处理程序来执行此操作,它的运行速度非常快且稳定。
我已决定探索具有相同程序的选项,该程序当前仅接收一种格式的XML文档,还可以接收两种添加的XML文档格式,并具有各种XML元素更改。我希望只是根据文档中的第一个" startElement"将ContentHandler与一个合适的交换出去...但是,嗯,设置ContentHandler然后解析该文档!
... constructor ... { SAXParserFactory spf = SAXParserFactory.newInstance(); try { SAXParser sp = spf.newSAXParser(); parser = sp.getXMLReader(); parser.setErrorHandler(new MyErrorHandler()); } catch (Exception e) {} ... parse StringBuffer ... try { parser.setContentHandler(pP); parser.parse(new InputSource(new StringReader(xml.toString()))); return true; } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } ...
因此,似乎并没有以我最初认为的方式可以做到这一点。
话虽这么说,我看这是完全错误的吗?用相同的XML处理代码解析多个离散XML文档的最佳方法是什么?我曾尝试在较一般的帖子中提问……但是,我认为我太含糊了。为了提高速度和效率,我从未真正看过DOM,因为这些XML文档相当大,系统每隔几分钟就会收到1200个左右的文档。这只是信息发送的一种方式
使这个问题过长并加深我的困惑;以下是一些我想拥有一个SAX,StAX或者??的XML文档的模型。解析器干净利落地处理。
products.xml:
<products> <product> <id>1</id> <name>Foo</name> <product> <id>2</id> <name>bar</name> </product> </products>
stores.xml:
<stores> <store> <id>1</id> <name>S1A</name> <location>CA</location> </store> <store> <id>2</id> <name>A1S</name> <location>NY</location> </store> </stores>
manager.xml:
<managers> <manager> <id>1</id> <name>Fen</name> <store>1</store> </manager> <manager> <id>2</id> <name>Diz</name> <store>2</store> </manager> </managers>
解决方案
回答
JAXB。 XML绑定的Java体系结构。基本上,我们将创建一个xsd来定义XML布局(我相信我们也可以使用DTD)。然后,将XSD传递给JAXB编译器,然后编译器创建Java类,以将XML文档编组和解编为Java对象。真的很简单。
顺便说一句,jaxb有命令行选项,以指定我们要在其中放置结果类的包名称,以此类推。
回答
我们已经很好地解释了我们想做什么,但没有解释原因。有几种XML框架可简化Java对象与XML之间的编组和解组。
最简单的是Commons Digester,我通常使用它来解析配置文件。但是,如果要处理Java对象,则应查看Castor,JiBX,JAXB,XMLBeans,XStream或者类似的对象。 Castor或者JiBX是我的两个最爱。
回答
我曾经尝试过SAXParser,但是一旦找到XStream,就再也没有使用它。使用XStream,我们可以创建Java对象并将其转换为XML。将它们发送过来,并使用XStream重新创建对象。非常易于使用,快速并且创建干净的XML。
无论哪种方式,我们都必须知道要从XML文件接收哪些数据。我们可以通过不同的方式将它们发送出去,以了解要使用的解析器。或者有一个数据对象可以容纳所有内容,但只能填充一个结构(产品/商店/经理)。也许像这样:
public class DataStructure { List<ProductStructure> products; List<StoreStructure> stors; List<ManagerStructure> managers; ... public int getProductCount() { return products.lenght(); } ... }
并用XStream转换为XML发送过来,然后重新创建对象。然后用它来做你想做的。
回答
据我了解,问题在于我们在解析之前不知道文档的格式。我们可以使用委托模式。我假设我们不针对DTD / XSD / etcetera进行验证,并且DefaultHandler具有状态是可以的。
public class DelegatingHandler extends DefaultHandler { private Map<String, DefaultHandler> saxHandlers; private DefaultHandler delegate = null; public DelegatingHandler(Map<String, DefaultHandler> delegates) { saxHandlers = delegates; } @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { if(delegate == null) { delegate = saxHandlers.get(name); } delegate.startElement(uri, localName, name, attributes); } @Override public void endElement(String uri, String localName, String name) throws SAXException { delegate.endElement(uri, localName, name); } //etcetera...
回答
请参阅XMLReader.setContentHandler()的文档,其中显示:
Applications may register a new or different handler in the middle of a parse, and the SAX parser must begin using the new handler immediately.
因此,我们应该能够创建一个使用SelectorContentHandler的事件,该事件将消耗事件直到第一个startElement事件为止,基于此,将更改XML阅读器上的ContentHandler并将第一个start元素事件传递给新的内容处理程序。我们只需要将XMLReader传递给构造函数中的SelectorContentHandler即可。如果我们需要将所有事件传递给特定于词汇的内容处理程序,则SelectorContentHandler必须先缓存事件,然后将其传递,但是在大多数情况下不需要这样做。
附带说明一下,我最近在几乎所有项目中都使用XOM来处理XML ja,到目前为止,性能并不是问题。
回答
如果我们需要更多动态处理,则Stax方法可能比Sax更好。
那还是很低级的。如果我们想要更简单的方法,XStream和JAXB是我的最爱。但是它们确实需要相当严格的对象才能映射到。
回答
同意StaxMan,有趣的是,他希望我们使用Stax。这是一个基于请求的解析器,而不是我们当前使用的请求。但是,这将需要对代码进行一些重大更改。
回答
:-)
是的,我对Stax有一些偏见。但是正如我所说,数据绑定通常比流解决方案更方便。但是,如果我们要流式传输,并且不需要流水线(多个过滤阶段),则Stax比SAX更简单。
还有一件事:与XOM一样好(可以替代),如果不处理"以文档为中心"的xml(〜= xhtml页面,docbook,开放式办公室文档),则通常不适合使用Tree Model。
对于数据交换,配置文件等,数据绑定更方便,更高效,更自然。对于这些用例,只需对DOM之类的树模型说不即可。
因此,JAXB,XStream,JibX都不错。或者,为了获得更多的口味,请使用蒸煮器,蓖麻和xmlbeans。