美文网首页
SAX解析复杂乱码XML文件List

SAX解析复杂乱码XML文件List

作者: 秋风送秋雨 | 来源:发表于2020-03-17 10:48 被阅读0次

    一、SAX解析的优点

    SAX解析是逐行解析XML,占用内存小,采用dom4j解析(一次读入整个xml文件)内存不足时建议采用SAX解析。

    二、SAX解析流程

    给定一个xml文件

     <?xml version="1.0" encoding="utf-8"?>
     <!DOCTYPE mdc SYSTEM "MeasDataCollection.dtd">
     <attr name="Info">NO���///�///�/�/���</attr>       
     <MOTree>
         <MO className="valueX1" fdn="valueX2">
             <attr name="key1">value1</attr>
             <attr name="key2">value2</attr>
             <attr name="key3">value3</attr>
             <MO className="valueY1" fdn="valueY2">
               <attr name="keyA1">valueA1</attr>
               <attr name="keyA2">valueA2</attr>
               <attr name="keyA3">valueA3</attr>
               <attr name="id">1</attr>
             </MO>
             <MO className="valueZ1" fdn="valueZ2">
               <attr name="keyB1">valueB1</attr>
               <attr name="keyB2">valueB2</attr>
               <attr name="keyB3">valueB3</attr>
               <attr name="id">3</attr>
             </MO>
             <MO className="valueXX1" fdn="valueXX2">
               <attr name="keyC1">valueC1</attr>
               <attr name="keyC2">valueC2</attr>
               <attr name="keyC3">valueC3</attr>
               <attr name="id">2</attr>
             </MO>
         </MO>
     </MOTree>
    

    本人工作中遇到的XML如上所示。不仅存在很多非法字符,而且还要考虑忽略解析dtd,以及文件过大不得不减少服务器读写压力(DOM解析文件时让服务器宕机了)。所以采取了SAX的方式来解析XML文件,并在解析前对XML文件进行预处理。

    1.处理非法字符

    样例中的xml文件涉及到非法字符,用不到的情况下需要做消去处理,避免读取xml文件时抛出0xdd异常,处理代码如下(写的不好请见谅)

    public static String XmlFileToStr(File xmlFile) {
    
            StringBuffer xmlString = new StringBuffer();
            char current;
            String temp;
            try {
                InputStream in = new FileInputStream(xmlFile);
                BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"), 10 * 1024 * 1024);
                while (reader.ready()) {
                    temp = reader.readLine();
                    for (int i = 0; i < temp.length(); i++) {
                        current = temp.charAt(i);
                        if ((current == 0x9) ||
                                //换行为0x0A或0xA
                                (current == 0xA) ||
                                (current == 0xD) ||
                                //空格为0x20
                                ((current >= 0x20) && (current <= 0xD7FF)) ||
                                ((current >= 0xE000) && (current <= 0xFFFD)) ||
                                ((current >= 0x10000) && (current <= 0x10FFFF))) {
                            xmlString.append(current);
                        }
                    }
                }
                reader.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return xmlString.toString();
        }
    

    2.建立要解析文本的bean类

    本次以解析XML中的id为例。
    这里用了lombok的几个方法,感兴趣的可以搜一下。

    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    /**
     * @author Amenoaki
     */
    @Setter
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    public class CmsBean {
        private String id;
    }
    
    

    3.定义Handler类继承DefaultHandler类,以便我们能选择性实现我们需要的方法

    DefaultHandler类常用方法解析

    startDocument() :读取文档开头时调用,可在此方法中进行预处理操作,比如:初始化bean类,或者容器

    endDocument() : 读取文档结束时调用,在此方法中进行结尾工作

    startElement() : 开始标签时触发

    endElment() : 结束标签时触发

    characters() : 处理文件中读取到内容,即标签间内容,注意:标签后的空格及tab键也会被读取到。

    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Amenoaki
     */
    public class CmHandler extends DefaultHandler {
    
        //标志解析到哪一个节点
        private String currentTag;
        private List<CmsBean> cmsData;
        private CmsBean cmBean;
        //判断数据是否重复
        private static String existDataOfId = "";
    
        public List<CmsBean> getCmsData() {
            return cmsData;
        }
    
        @Override
        public void startDocument() throws SAXException {
            super.startDocument();
            cmsData = new ArrayList<>(10);
        }
    
        @Override
        public void endDocument() throws SAXException {
            super.endDocument();
        }
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            super.startElement(uri, localName, qName, attributes);
            if (qName.equals("attr")) {
                if (attributes.getValue(0).equals("id")) {
                    cmBean = new CmsBean();
                    this.currentTag = attributes.getValue(0);
                }
            }
        }
    
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            super.endElement(uri, localName, qName);
            if (qName.equals("attr") && this.cmBean != null) {
                //当数据不重复时,存入
                if (!existDataOfId.equals(this.cmBean.getId())) {
                    existDataOfId = this.cmBean.getId();
                    this.cmsData.add(this.cmBean);
                } 
            }
            this.currentTag = null;
        }
    
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            super.characters(ch, start, length);
            String tagName = this.currentTag;
            //避免拿到空值
            if (tagName != null && !tagName.equals("") && tagName.length() > 0 && this.cmBean != null) {
                //data为解析后得到的数据
                String data = new String(ch, start, length);
    
                //给CM对象赋值
                setCm(tagName, data);
            }
        }
    
        public void setCm(String tagName, String data) {
            //需要多个属性值可以作不同判断
            switch (tagName) {
                case "id":
                    this.cmBean.setId(data);
                    break;
                default:
                    break;
            }
        }
    }
    
    

    开始使用我们的SAX解析

    
    public static List<CmsBean> xmlToMapBySAX(String xml){
            List<CmsBean> cmsData = new ArrayList<>();
            try{
                SAXParserFactory factory=SAXParserFactory.newInstance();
                SAXParser saxParser=factory.newSAXParser();
                CmHandler cmHandler = new CmHandler();
                //根据情况更改字体编码,此处样例为UTF_8.
                saxParser.parse(new InputSource(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))), cmHandler);
                cmsData = cmHandler.getCmsData();
            }catch (Exception e){
                e.printStackTrace();
            }
            return cmsData;
        }
    

    4.main函数示例

    public List<Map<String, Object>> parseFile(File file) {
    
            String xml = RanXmlParse.XmlFileToStr(file);
            List<Map<String, Object>> list = new ArrayList<>();
            List<CmsBean> cmsBeans = TreeUtil.xmlToMapBySAX(xml);
    
            for (CmsBean cmsDatum : cmsBeans) {
                Map<String, Object> map = new HashMap(16);
                //有多个属性时酌情修改
                map.put("id", cmsDatum.getId());
                list.add(map);
            }
    
            return list;
        }
    

    参考资料
    SAX解析XML文件

    相关文章

      网友评论

          本文标题:SAX解析复杂乱码XML文件List

          本文链接:https://www.haomeiwen.com/subject/jvvwehtx.html