java中使用SAX读取和写出XML文件

来源:互联网 发布:乐华电视软件下载 编辑:程序博客网 时间:2024/06/06 01:07

原文链接:http://blog.csdn.net/chjttony/article/details/7610483

SAX是一种事件驱动的流式XML文件处理方式,区别与DOM方式的是不需要在内存中建一棵DOM树,而是根据读取XML时遇到的标签事件来顺序处理,因此具有速度快,内存占用上的优点。SAX往往是大容量XML文件处理的首选方法,SAX读取XML相对比较简单,但是写XML就稍微比DOM方式复杂一些,网上的例子也不够全面和详细,刚好在工作中用到了XML读取和写出XML,记录下来以供参考。

读取XML文件:

首先,要读取的目标XML文件如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <oes:Notifications xmlns:oes="http://xml.sax.test.com/oesAccessNotification">  
  3.     <oes:Notification>  
  4.         <oes:NotificationID>11111</oes:NotificationID>  
  5.         <oes:NotificationType>AlarmNew</oes:NotificationType>  
  6.         <oes:timeStamp>2009-02-25T08:57:17</oes:timeStamp>  
  7.         <oes:Appendix>  
  8.             <oes:MapItem key="key" value="value"/>  
  9.         </oes:Appendix>  
  10.         <oes:Content>  
  11.             <alarmNew systemDN="PLMN-1/S3SN-1/SRME-BSS-2/SBSS-0">  
  12.                 <alarmId>400951</alarmId>  
  13.                 <alarmText>PIPE 0 IS SLOW OR NOT WORKING</alarmText>  
  14.                 <eventTime>2009-02-25T08:57:17+02:00</eventTime>  
  15.                 <eventType>processingError</eventType>  
  16.                 <perceivedSeverity>critical</perceivedSeverity>  
  17.                 <probableCause>0</probableCause>  
  18.                 <specificProblem>86600</specificProblem>  
  19.                 <additionalText1>A Raised by pipe supervision script, process ID 20848</additionalText1>  
  20.                 <additionalText2>A test additional text2</additionalText2>  
  21.                 <additionalText3>A test additional text3</additionalText3>  
  22.                 <additionalText4>A test additional text4</additionalText4>  
  23.                 <additionalText5>A Original Additional text: test alarm1 | Original Probable Cause: Toxic Leak1 Detected |  
  24.                     Original alarm time: 20090901183006+0530 | Automatic clearing:Y  
  25.                 </additionalText5>  
  26.                 <additionalText6>Original  
  27.                 </additionalText6>  
  28.             </alarmNew>  
  29.         </oes:Content>  
  30.     </oes:Notification>  
  31. </oes:Notifications>  
SAX读取该XML文件的过程如下:

(1).定义XML中各种标签:

[java] view plain copy
  1. class Constant {  
  2.     public static final String NAME_SPACE = "xmlns:oes";  
  3.     public static final String SCHEMA = "http://xml.sax.test.com/oesAccessNotification";          
  4.     public static final String NOTIFICATIONS = "oes:Notifications";          
  5.     public static final String NOTIFICATION = "oes:Notification";  
  6.         public static final String NOTIFICATION_ID = "oes:NotificationID";  
  7.     public static final String NOTIFICATION_TYPE ="oes:NotificationType";  
  8.     public static final String TIME_STAMP = "oes:timeStamp";  
  9.     public static final String APPENDIX = "oes:Appendix";  
  10.     public static final String MAP_ITEM = "oes:MapItem";  
  11.     public static final String KEY = "key";  
  12.     public static final String VALUE = "value";  
  13.     public static final String CONTENT = "oes:Content";  
  14.     public static final String ALARM_NEW = "alarmNew";  
  15.     public static final String SYSTEM_DN = "systemDN";  
  16.     public static final String ALARM_ID = "alarmId";  
  17.     public static final String ALRAM_TEXT = "alarmText";  
  18.     public static final String EVENT_TIME = "eventTime";  
  19.     public static final String EVENT_TYPE = "eventType";  
  20.     public static final String PERCEIVED_SEVERITY = "perceivedSeverity";  
  21.     public static final String PROBABLE_CAUSE= "probableCause";  
  22.     public static final String SPECIFIC_PROBLEM = "specificProblem";  
  23.     public static final String ADDITION_TEXT1 = "additionalText1";  
  24.     public static final String ADDITION_TEXT2 = "additionalText2";  
  25.     public static final String ADDITION_TEXT3 = "additionalText3";  
  26.     public static final String ADDITION_TEXT4 = "additionalText4";  
  27.     public static final String ADDITION_TEXT5 = "additionalText5";  
  28.     public static final String ADDITION_TEXT6 = "additionalText6";  
  29.     public static final String ADDITION_TEXT7 = "additionalText7";  
  30. }  

这些定义会在读取XML的处理过程中用到。

(2).定义XML文件节点对应的java对象:

[java] view plain copy
  1. class EventFactory {  
  2.   
  3.     private XMLReader xmlReader;  
  4.   
  5.     public static class InternalEvent {  
  6.         private String notificationType = "";  
  7.           
  8.         private Map<String, String> props = new HashMap<String, String>();  
  9.   
  10.         public String getNotificationType() {  
  11.             return notificationType;  
  12.         }  
  13.           
  14.         public String getProp(String name) {  
  15.             String str = props.get(name);  
  16.             if (str == null) {  
  17.                 return "";  
  18.             } else {  
  19.                 return str;  
  20.             }  
  21.         }  
  22.           
  23.         public Map<String, String> getProps(){  
  24.             return props;  
  25.         }  
  26.   
  27.         public void setNotificationType(String notificationType) {  
  28.             this.notificationType = notificationType;  
  29.         }  
  30.   
  31.         public void putAttribute(String name, String value) {  
  32.             this.props.put(name, value);  
  33.         }  
  34.   
  35.     }  
  36.     //调用SAX读取XML的方法,XML文件的数据会被存放到该List中  
  37.     public List<InternalEvent> read(String xmlPath) throws ParserConfigurationException, SAXException {  
  38.         SAXParserFactory spf = SAXParserFactory.newInstance();  
  39.         SAXParser saxParser = spf.newSAXParser();  
  40.           
  41.         xmlReader = saxParser.getXMLReader();  
  42.         List<InternalEvent> container = new LinkedList<InternalEvent>();  
  43.         ContentHandler handler = new ReadXMLHandler(container);  
  44.         xmlReader.setContentHandler(handler);  
  45.         try {  
  46.             xmlReader.parse(new InputSource(xmlPath));  
  47.         } catch (IOException e) {  
  48.             e.printStackTrace();  
  49.         }  
  50.         return container;  
  51.     }  
  52.       
  53. }  
该对象会在SAX读取XML文件时,将XML数据转换为内存中的java对象。

(3).SAX读取XML文件:

[java] view plain copy
  1. class ReadXMLHandler extends DefaultHandler {  
  2.   
  3.     private List<EventFactory.InternalEvent> eventContainer;  
  4.     private StringBuilder buf = new StringBuilder();  
  5.     private EventFactory.InternalEvent event;  
  6.     private static final Set<String> ATTR_TAGS = new HashSet<String>();  
  7.   
  8.     static {  
  9.         ATTR_TAGS.add(Constant.EVENT_TIME);  
  10.         ATTR_TAGS.add(Constant.SPECIFIC_PROBLEM);  
  11.         ATTR_TAGS.add(Constant.ALRAM_TEXT);  
  12.         ATTR_TAGS.add(Constant.PERCEIVED_SEVERITY);  
  13.         ATTR_TAGS.add(Constant.ADDITION_TEXT1);  
  14.         ATTR_TAGS.add(Constant.ADDITION_TEXT2);  
  15.         ATTR_TAGS.add(Constant.ADDITION_TEXT3);  
  16.         ATTR_TAGS.add(Constant.ADDITION_TEXT4);  
  17.         ATTR_TAGS.add(Constant.ADDITION_TEXT5);  
  18.         ATTR_TAGS.add(Constant.ADDITION_TEXT6);  
  19.         ATTR_TAGS.add(Constant.ADDITION_TEXT7);  
  20.         ATTR_TAGS.add(Constant.EVENT_TYPE);  
  21.     }  
  22.   
  23.     public ReadXMLHandler(List<EventFactory.InternalEvent> eventContainer) {  
  24.         this.eventContainer = eventContainer;  
  25.     }  
  26.   
  27.     @Override  
  28.     public void startElement(String uri, String localName, String qName,  
  29.             Attributes attributes) throws SAXException {  
  30.         buf.setLength(0);  
  31.         if (qName.equals("oes:Notification")) {  
  32.             event = new EventFactory.InternalEvent();  
  33.             eventContainer.add(event);  
  34.         }  
  35.         else if(qName.equals(Constant.MAP_ITEM)){  
  36.             //获取元素中的属性值,如<a key="key" value="value"/>,获取key和value  
  37.             String key = attributes.getValue(Constant.KEY);  
  38.             event.putAttribute(Constant.KEY, key);  
  39.             String value = attributes.getValue(Constant.VALUE);  
  40.             event.putAttribute(Constant.VALUE, value);  
  41.         }  
  42.         else if(qName.equals(Constant.ALARM_NEW)){  
  43.             String systemDn = attributes.getValue(Constant.SYSTEM_DN);  
  44.             event.putAttribute(Constant.SYSTEM_DN, systemDn);  
  45.         }  
  46.     }  
  47.   
  48.     @Override  
  49.     public void endElement(String uri, String localName, String qName)  
  50.             throws SAXException {  
  51.         if (qName.equals(Constant.NOTIFICATION_TYPE)) {  
  52.             event.setNotificationType(buf.toString());  
  53.         }   
  54.         else if (ATTR_TAGS.contains(qName)){  
  55.             event.putAttribute(qName, buf.toString());  
  56.         }  
  57.     }  
  58.   
  59.     //获取元素值,如<a>abc</a>,获取其中的abc  
  60.     @Override  
  61.     public void characters(char[] ch, int start, int length)  
  62.             throws SAXException {  
  63.         buf.append(ch, start, length);  
  64.     }  
  65.   
  66. }  
至此XML就读取成功

转换并写出XML文件:

比起SAX读取XML来,SAX写XML要相对复杂一些,流程如下:

(1).对读取的XML对象做一个简单的转换:

[java] view plain copy
  1. class Convert {  
  2.     public static String convertString(String value){  
  3.         return value + "_TEST";  
  4.     }   
  5. }  
转换很简单,即将XML标签加一个“_TEST”,同时给值也加一个“_TEST“

(2).写XML文件:

[java] view plain copy
  1. class WriteXML {  
  2.   
  3.     SAXTransformerFactory fac = (SAXTransformerFactory) SAXTransformerFactory.newInstance();  
  4.   
  5.     private TransformerHandler handler = null;  
  6.     private OutputStream outStream = null;  
  7.     private String fileName;  
  8.     private AttributesImpl atts;  
  9.     private String rootElement;  
  10.     //元素层次,用于控制XML缩进  
  11.     private static int level = 0;  
  12.     //每个层次父级缩进4个空格,即一个tab  
  13.     private static String tab = "    ";  
  14.     //系统换行符,Windows为:"\n",Linux/Unix为:"/n"  
  15.     private static final String separator = System.getProperties().getProperty("os.name").toUpperCase().indexOf("WINDOWS") != -1 ? "\n" : "/n";  
  16.       
  17.     public WriteXML(String fileName, String rootElement) {  
  18.         this.fileName = fileName;  
  19.         this.rootElement = rootElement;  
  20.         init();  
  21.     }  
  22.   
  23.     public void init() {  
  24.         try {  
  25.             handler = fac.newTransformerHandler();  
  26.             Transformer transformer = handler.getTransformer();  
  27.             //设置输出采用的编码方式  
  28.             transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");  
  29.             //是否自动添加额外的空白  
  30.             transformer.setOutputProperty(OutputKeys.INDENT, "yes");  
  31.             //是否忽略xml声明  
  32.             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");  
  33.             outStream = new FileOutputStream(fileName);  
  34.             Result resultxml = new StreamResult(outStream);  
  35.             handler.setResult(resultxml);  
  36.             atts = new AttributesImpl();  
  37.             start();  
  38.         }catch (Exception e) {  
  39.             e.printStackTrace();  
  40.         }  
  41.     }  
  42.   
  43.     private void start() {  
  44.         try {  
  45.             handler.startDocument();  
  46.             //设置schema和名称空间  
  47.             atts.addAttribute("""", Constant.NAME_SPACE, String.class.getName(), Constant.SCHEMA);  
  48.             handler.startElement("""", rootElement, atts);  
  49.         } catch (Exception e) {  
  50.             e.printStackTrace();  
  51.         }  
  52.     }  
  53.   
  54.     //元素里面会嵌套子节点,因此元素的开始和结束分开写  
  55.     //如:<a><b>bcd</b></a>  
  56.     private void startElement(String objectElement, AttributesImpl attrs)  
  57.             throws SAXException {  
  58.         if(attrs == null){  
  59.             attrs = new AttributesImpl();  
  60.         }  
  61.         level++;  
  62.         appendTab();  
  63.         if (objectElement != null) {  
  64.             //注意,如果atts.addAttribute设置了属性,则会输出如:<a key="key" value="value">abc</a>格式  
  65.             //如果没有设置属性,则输出如:<a>abc</a>格式  
  66.             handler.startElement("""", objectElement, attrs);  
  67.         }     
  68.     }  
  69.       
  70.     //正常元素结束标记,如:</a>  
  71.     private void endElement(String objectElement) throws SAXException{  
  72.         level--;  
  73.         appendTab();  
  74.         if (objectElement != null) {  
  75.             handler.endElement("""", objectElement);  
  76.         }  
  77.     }  
  78.   
  79.     //自封闭的空元素,如<a key="key" value="value"/>,不用换行,写在一行时XML自动会自封闭  
  80.     private void endEmptyElement(String objectElement) throws SAXException{  
  81.         handler.endElement("""", objectElement);  
  82.     }  
  83.       
  84.     //无子节点的元素成为属性,如<a>abc</a>  
  85.     private void writeAttribute(String key, String value) throws SAXException{  
  86.         atts.clear();  
  87.         level++;  
  88.         appendTab();  
  89.         handler.startElement("""", key, atts);  
  90.         handler.characters(value.toCharArray(), 0, value.length());  
  91.         handler.endElement("""", key);  
  92.         level--;  
  93.     }  
  94.   
  95.     public void end() {  
  96.         try {  
  97.             handler.endElement("""", rootElement);  
  98.             // 文档结束,同步到磁盘  
  99.             handler.endDocument();  
  100.             outStream.close();  
  101.         }catch (Exception e) {  
  102.             e.printStackTrace();  
  103.         }  
  104.     }  
  105.   
  106.     //Tab缩进,SAX默认不自动缩进,因此需要手动根据元素层次进行缩进控制  
  107.     private void appendTab() throws SAXException{  
  108.         String indent = separator + "    ";  
  109.         for(int i = 1 ; i< level; i++){  
  110.            indent += tab;  
  111.        }  
  112.         handler.characters(indent.toCharArray(), 0, indent.length());  
  113.     }  
  114.       
  115.     public void writeNotification(InternalEvent event) throws SAXException{  
  116.         Map<String, String> props = event.getProps();  
  117.         Set<String> keys = props.keySet();  
  118.           
  119.         level = 0;  
  120.         //写<oes:Notification>节点  
  121.         startElement(Constant.NOTIFICATION, null);  
  122.           
  123.         //写oes:NotificationID  
  124.         writeAttribute(Convert.convertString(Constant.NOTIFICATION_ID), Convert.convertString(props.get(Constant.NOTIFICATION_ID)));  
  125.         keys.remove(Constant.NOTIFICATION_ID);  
  126.         //写oes:NotificationType  
  127.         writeAttribute(Convert.convertString(Constant.NOTIFICATION_TYPE), Convert.convertString(event.getNotificationType()));  
  128.         //写oes:timeStamp  
  129.         writeAttribute(Convert.convertString(Constant.TIME_STAMP), Convert.convertString(props.get(Constant.TIME_STAMP)));  
  130.         keys.remove(Constant.TIME_STAMP);  
  131.           
  132.         //写<oes:Appendix>节点  
  133.         startElement(Constant.APPENDIX, null);  
  134.         //写oes:MapItem  
  135.         atts = new AttributesImpl();  
  136.         atts.addAttribute("""", Convert.convertString(Constant.KEY), String.class.getName(), Convert.convertString(props.get(Constant.KEY)));  
  137.         keys.remove(Constant.KEY);  
  138.         atts.addAttribute("""", Convert.convertString(Constant.VALUE), String.class.getName(), Convert.convertString(props.get(Constant.VALUE)));  
  139.         keys.remove(Constant.VALUE);  
  140.         startElement(Constant.MAP_ITEM, atts);  
  141.         //结束oes:MapItem,由于MapItem是个自封闭的元素,需要特殊处理  
  142.         endEmptyElement(Constant.MAP_ITEM);  
  143.         keys.remove(Constant.MAP_ITEM);  
  144.         //结束oes:MapItem节点  
  145.         endElement(Constant.APPENDIX);  
  146.         keys.remove(Constant.APPENDIX);  
  147.           
  148.         //写oes:Content节点  
  149.         startElement(Constant.CONTENT, null);  
  150.         keys.remove(Constant.CONTENT);  
  151.           
  152.         //写alarmNew节点  
  153.         atts = new AttributesImpl();  
  154.         atts.addAttribute("""", Convert.convertString(Constant.SYSTEM_DN), String.class.getName(), Convert.convertString(props.get(Constant.SYSTEM_DN)));  
  155.         startElement(Constant.ALARM_NEW, atts);  
  156.         keys.remove(Constant.ALARM_NEW);  
  157.           
  158.         //写Alarm节点内的属性  
  159.         for(String key : keys){  
  160.             writeAttribute(Convert.convertString(key), Convert.convertString(props.get(key)));  
  161.         }  
  162.           
  163.         //结束alarmNew节点  
  164.         endElement(Constant.ALARM_NEW);  
  165.           
  166.         //结束oes:Content节点  
  167.         endElement(Constant.CONTENT);  
  168.           
  169.         //结束<oes:Notification>节点  
  170.         endElement(Constant.NOTIFICATION);  
  171.     }  
  172. }   
(3).先用SAX读取XML文件,然后使用SAX处理写出的demo程序如下:

[java] view plain copy
  1. public class FlexMapping {  
  2.     private static String inputFile = "input/input.xml";  
  3.     private static String outputFile = "output/output.xml";  
  4.     private static List<InternalEvent> events;  
  5.     public static void main(String[] args) {  
  6.         long start = System.currentTimeMillis();  
  7.         try {  
  8.             events = new EventFactory().read(inputFile);  
  9.             WriteXML xml = new WriteXML(outputFile, Constant.NOTIFICATIONS);  
  10.             for(InternalEvent event : events){  
  11.                 xml.writeNotification(event);  
  12.             }  
  13.             xml.end();  
  14.         }catch (Exception e) {  
  15.             e.printStackTrace();  
  16.         }  
  17.         System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms.");  
  18.     }  
  19. }  
写出的XML文件如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <oes:Notifications xmlns:oes="http://xml.sax.test.com/oesAccessNotification">  
  3.     <oes:Notification>  
  4.         <oes:NotificationID_TEST>11111_TEST</oes:NotificationID_TEST>  
  5.         <oes:NotificationType_TEST>AlarmNew_TEST</oes:NotificationType_TEST>  
  6.         <oes:timeStamp_TEST>2009-02-25T08:57:17_TEST</oes:timeStamp_TEST>  
  7.         <oes:Appendix>  
  8.             <oes:MapItem key_TEST="key_TEST" value_TEST="value_TEST"/>  
  9.         </oes:Appendix>  
  10.             <oes:Content>  
  11.                 <alarmNew systemDN_TEST="PLMN-1/S3SN-1/SRME-BSS-2/SBSS-0_TEST">  
  12.                     <additionalText1_TEST>A Raised by pipe supervision script, process ID 20848_TEST</additionalText1_TEST>  
  13.                     <systemDN_TEST>PLMN-1/S3SN-1/SRME-BSS-2/SBSS-0_TEST</systemDN_TEST>  
  14.                     <additionalText2_TEST>A test additional text2_TEST</additionalText2_TEST>  
  15.                     <eventTime_TEST>2009-02-25T08:57:17+02:00_TEST</eventTime_TEST>  
  16.                     <probableCause_TEST>0_TEST</probableCause_TEST>  
  17.                     <additionalText3_TEST>A test additional text3_TEST</additionalText3_TEST>  
  18.                     <alarmText_TEST>PIPE 0 IS SLOW OR NOT WORKING_TEST</alarmText_TEST>  
  19.                     <specificProblem_TEST>86600_TEST</specificProblem_TEST>  
  20.                     <additionalText6_TEST>Original_TEST</additionalText6_TEST>  
  21.                     <additionalText5_TEST>A Original Additional text: test alarm1 | Original Probable Cause: Toxic Leak1 Detected |  
  22.                     Original alarm time: 20090901183006+0530 | Automatic clearing:Y  
  23.                 _TEST</additionalText5_TEST>  
  24.                     <perceivedSeverity_TEST>critical_TEST</perceivedSeverity_TEST>  
  25.                     <additionalText4_TEST>A test additional text4_TEST</additionalText4_TEST>  
  26.                     <alarmId_TEST>400951_TEST</alarmId_TEST>  
  27.                     <eventType_TEST>processingError_TEST</eventType_TEST>  
  28.             </alarmNew>  
  29.         </oes:Content>  
  30.     </oes:Notification>  
  31. </oes:Notifications>  
Demo例子,仅供参考,不过已经基本上涵盖了SAX读写XML的流程。