用Java 处理 XML 数据

来源:互联网 发布:windows qt 串口编程 编辑:程序博客网 时间:2024/06/05 14:47

看起来这是个常规性的动作。不知道什么时候你就需要用JAVA处理一下XML。

虽说现在用XML作为媒体进行数据交换不是那么有效率(你可以试着Google “为啥不用XML” 之类的了解了解),不过鉴于很多古板的系统只能支持长胡子的技术实现,所以这方面也要照顾一下退休人士。

了解背景会让你聊天的时候显得牛一些。

Java原生内置的处理XML的技术基本有这么几种:DOM,SAX,Stax,Jaxb


DOM :Document Object Model 顾名思义就是在内存中构建树形结构。处理小的XML文件还算勉强应付。如果文件比较大,他需要一次性装载整个XML,你会忍不了他的速度,并且他会吃光你所有的内存,最后程序会负分滚粗。

SAX:Simple API for XML Parsing 一般名字应该是没实现的愿望体现。比如一个人如果叫王金库,那么可以肯定他绝对没有金库。这样你应该理解这个API为啥叫Simple了。这API是回调式的,也就是你写的程序是被别人调用的。这API比DOM快点,而且是可以部分装载XML,这样你不用害怕OOME了。啥?你想写XML?忘掉这个叫Simple的玩意儿吧。

Stax: Streaming API for XML 这个总算是靠点谱了。你写的程序是主动式的访问XML各个节点。流式访问,不用担心OOME,速度嘛算是原生态里面最好的了。而且读写都支持,你不用这个还用哪个啊?


给个Stax的代码片段,算是参考吧:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. XMLInputFactory xif = XMLInputFactory.newInstance();  
  2. XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));  
  3. xsr.nextTag(); // Advance to statements element  
  4. long i = 0;  
  5. String action = null;  
  6. while (xsr.hasNext()) {  
  7.     if (xsr.next() == XMLStreamConstants.START_ELEMENT) {  
  8.         if ("ContentItem".equals(xsr.getLocalName())) {  
  9.             action = getAttributeValue(xsr, "action");  
  10.         } else if ("Data".equals(xsr.getLocalName())) {  
  11.             i ++;  
  12.         }  
  13.     }  
  14. }  

JAXB:Java Architecture for XML Binding 这是一个很酷的API。想法就是把XML各种属性定义映射到普通的Java Bean上。你无须关心解析反解析的细节,只要用普通的Java Bean就完成和XML的交互了。可是怎么构建一一映射的JavaBean呢?在已知XML的定义的情况下你可以用自带的xjc 命令自动生成相对应的JavaBean。如果你用Eclipse,有类似的插件帮你完成这步。具体可以google一下。然后你要做的就是仅仅是使用数据啦。简单的令人发指:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. JAXBContext jc = JAXBContext.newInstance("com.your.xml.datatype.bean"); // 先注册你的JavaBean  
  2. // Create unmarshaller  
  3. Unmarshaller um = jc.createUnmarshaller();  
  4. // Unmarshal XML contents of the file myDoc.xml into your Java object  
  5. // instance  
  6. ObjectFactory objFac = new ObjectFactory(); // 生成Bean之后你会有这个工厂类的  
  7. objFac.createYourData(objFac.createYourDataType());  
  8. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("your.xml")); // 你要怎么打开你的XML文件呢?  
  9. JAXBElement myJAXBObject = (JAXBElement) um.unmarshal(bis); // 读取  
  10. YourDataType yourData = (YourDataType) myJAXBObject.getValue(); // 可以用啦  
  11.   
  12. // 下面是写XML的例子  
  13. Marshaller m = jc.createMarshaller();  
  14. m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);  
  15. File outfile = new File("yourOutput.xml");  
  16. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outfile), 4096);  
  17. m.marshal(myJAXBObject, bos); // 一步写入。 myJAXBObject 需要你自己构建,你要存什么呢  
  18.   
  19. try {  
  20.     bos.flush();  
  21. catch (IOException e) {  
  22.     // TODO Auto-generated catch block  
  23.     e.printStackTrace();  
  24. finally {  
  25.     try {  
  26.         bos.close();  
  27.     } catch (Exception e) {  
  28.         // TODO Auto-generated catch block  
  29.         e.printStackTrace();  
  30.     }  
  31. }  

你也许意识到问题了:我去,这一下子把XML Load到内存里,你是不是疯了?为了避免疯掉,你可以用Satx啊,那玩意儿不是流式的么?给个栗子,读取这样的XML文件片段:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <OutXMLData>  
  2.     <SubXMLItemType>  
  3.     ....  
  4.     </SubXMLItemType>  
  5.     <SubXMLItemType>  
  6.     ....  
  7.     </SubXMLItemType>  
  8.     <SubXMLItemType>  
  9.     ....  
  10.     </SubXMLItemType>  
  11.     ....  
  12. </OutXMLData>  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private static void readWriteWithStAXAndJAXB() throws FactoryConfigurationError, FileNotFoundException, XMLStreamException, UnsupportedEncodingException, JAXBException,  
  2.         PropertyException {  
  3.     // set up a StAX reader  
  4.     XMLInputFactory xmlif = XMLInputFactory.newInstance();  
  5.     BufferedInputStream bis = new BufferedInputStream(new FileInputStream("inputLarge.xml"));  
  6.     XMLStreamReader xmlr = xmlif.createXMLStreamReader(bis);  
  7.   
  8.     File outfile = new File("output\\outfile.xml");  
  9.     OutputStreamWriter bos = new OutputStreamWriter(new FileOutputStream(outfile), "UTF-8");  
  10.     XMLOutputFactory xmlof = XMLOutputFactory.newInstance();  
  11.     XMLStreamWriter xmlw = xmlof.createXMLStreamWriter(bos);  
  12.     xmlw.writeStartDocument("UTF-8""1.0");  
  13.     xmlw.writeStartElement("OutXMLData");  
  14.   
  15.     JAXBContext ucontext = JAXBContext.newInstance(SubXMLItemType.class);  
  16.   
  17.     Unmarshaller unmarshaller = ucontext.createUnmarshaller();  
  18.     Marshaller marshaller = ucontext.createMarshaller();  
  19.     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);  
  20.     marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);  
  21.   
  22.     xmlr.nextTag();  
  23.     xmlr.require(XMLStreamConstants.START_ELEMENT, null"OutXMLData");  
  24.   
  25.     xmlr.nextTag();  
  26.     int iCount = 0;  
  27.     while (xmlr.getEventType() == XMLStreamConstants.START_ELEMENT) { // 按标签流式读取  
  28.         iCount++;  
  29.         JAXBElement<SubXMLItemType> pt = unmarshaller.unmarshal(xmlr, SubXMLItemType.class); // 只读取映射SubItem的内容  
  30.         marshaller.marshal(pt, xmlw); // 这步是分批流式写入  
  31.         xmlw.writeCharacters("\n");  
  32.         if (xmlr.getEventType() == XMLStreamConstants.CHARACTERS) {  
  33.             xmlr.next();  
  34.         }  
  35.     }  
  36.     xmlw.flush();  
  37.     xmlw.writeEndElement();  
  38.     xmlw.writeEndDocument();  
  39.     System.out.println("Entity Count is :" + iCount);  
  40.     xmlr.close();  
  41.     xmlw.close();  
  42. }  

说完这么多,基本上用Java处理XML已经不是难事了。不过,有时候你会有:给你蟹八件儿,你也无从下嘴的感受。比如,解析XML你可以掌控,随便你用啥,可是你调用的下游程序接口却需要另外一种格式的数据。比如,你用Stax解析XML,下游要DOM接口会不会令你抓狂起来?心里咒骂,倒霉玩意儿,你们还有没有点上进心?!最近我就遇到这事了,解析一个大的XML,下游要Sub的XML,或者叫XML片段,或者叫分割XML文件。好么,我把数据都拆成Java的Object,然后再给你拼成一个个小的XML文件发过去,丧心病狂么这不?!你如果真这么做了,就别往下看了,你会哭的!

Java 的XML包下面有个transform的子包,看看里面会有惊喜的。可以用这个工具包帮你完成类似的转换,比如Stax 和 Sax 或者Dom 互相的变换。或者变换成Stream。

拿我这个分割XML的小栗子来说:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. XMLInputFactory xif = XMLInputFactory.newInstance();  
  2. XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml")); // 用Stax读取XML  
  3. xsr.nextTag(); // Advance to statements element  
  4.   
  5. TransformerFactory tf = TransformerFactory.newInstance();  
  6. Transformer t = tf.newTransformer();  
  7. t.setParameter(OutputKeys.OMIT_XML_DECLARATION, "no");  
  8. t.setParameter(OutputKeys.STANDALONE, "yes");  
  9. long i = 0;  
  10. String action = null;  
  11. while (xsr.hasNext()) {  
  12.     if (xsr.next() == XMLStreamConstants.START_ELEMENT) {  
  13.         if ("ContentItem".equals(xsr.getLocalName())) {  
  14.             action = getAttributeValue(xsr, "action");  
  15.         } else if ("Data".equals(xsr.getLocalName())) {  
  16.             File file = new File("out/" + action + i++ + ".xml");  
  17.             t.transform(new StAXSource(xsr), new StreamResult(file)); // 流式变换,走你~  
  18.   
  19.             // DOMResult dr = new DOMResult(); // 如果你要Dom格式的,releaseMe  
  20.             // t.transform(new StAXSource(xsr), dr);  
  21.               
  22.         }  
  23.     }  
  24. }  

知道最变态的是什么吗?需要解析XML整个内容到String里面,不单单是数据,就是整个XML标签和数据。其实就是ouputStream转String的过程:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  2. t.transform(new StAXSource(xsr), new StreamResult(baos));  
  3. String subXMLStr = baos.toString();  


0 0