• 解析xml时遇到的一些问题


    http://www.blogjava.net/dongbule/archive/2010/09/28/333262.html

    先感谢同事renial的<解析xml时遇到的一些问题>技术分享,下面是一些记录和实际操作

    1.使用Dom4j解析大文件时内存溢出的问题

    问题是这样的,当我用dom4j去解析一个几十M的xml时,就出现out of memory.当然了,这也是根据你的机器性能而定的,我们都知道dom4j在各种DOM解析器中应该算是性能最好的,连大名鼎鼎的Hibernate都 是用dom4j来解析XML配置文件的
    问题出在于使用dom4j的SAXReader是会把整个XML文件一次性读入,如果XML文件过大就会抛出out of memory,但即使是使用SAXParser批量读入解析,但它也是一次解析完,假设XML文件有几万条数据,那么解析后就必须在内存放入这几万条对 象.

    常用的Dom4j文件解析方式:

    InputStream is = new FileInputStream(filePath);
    SAXReader reader 
    = new SAXReader();  //将整个XML构建为一个Document
    Document doc = reader.read(is);
    Element root 
    = doc.getRootElement(); // 获得根节点
    for (Object obj : root.elements()) { // 遍历每个节点
    Element e = (Element)obj;            // 对当前节点进行操作
    }


    解决方法:使用ElementHandler解析文件

    通过查API可以发现ElementHandler接口,下面是接口的介绍

    ElementHandler interface defines a handler of Element objects. It is used primarily in event based processing models such as for
    processing large XML documents as they are being parsed rather than waiting until the whole document is parsed.


    好了,它就是我们想要的,通过实现以下两个method,就可以达成我们的需求

    onEnd(ElementPath elementPath)
              Called by an event based processor when an elements closing tag is encountered.
    onStart(ElementPath elementPath)
              Called by an event based processor when an elements openning tag is encountered.

    下面是代码

    FileInputStream fis = new FileInputStream(addPath);
    SAXReader reader 
    = new SAXReader();
    ElementHandler addHandler 
    = new MyElementHandler();  //建立 MyElementHandler 的实例
    reader.addHandler("/root/test1", addHandler); // 节点
    reader.addHandler("/root/test2", addHandler); // 节点
    reader.read(fis);


    ...

    class MyElementHandler implements ElementHandler {
      
    public void onStart(ElementPath ep) {}
      
      
    public void onEnd(ElementPath ep) {
        Element e 
    = ep.getCurrent(); // 获得当前节点
        
    // 对节点进行操作。。。
        e.detach(); // 处理完当前节点后,将其从dom树中剪除
      }
    }


    因为每次处理完一个节点后并没有保存在dom树中,所以不会出现内存溢出的情况
    上面的省略了一些业务代码,不知道你是否明白或有更好的方法,可以和我联系QQ:34174409

    2.BOM头问题

    使用java.io.Reader读取XML文件进行解析时出现异常
    org.dom4j.DocumentException: Error on line 1 of document : Content is not allowed in prolog.
    Nested exception:
    org.xml.sax.SAXParseException: Content is not allowed in prolog.
    原因在于:UTF-8编码文件存在BOM头,Reader类无法正确识别

    解决方法:
    (1).使用16进制编辑器手动删除BOM头

    这个...自行解决

    (2).InputStream读取流中前面的字符,看是否有BOM,如果有BOM,干掉BOM头

    PushbackInputStream pis = new PushbackInputStream(in);
    int ch = pis.read();
    if (ch != 0xEF){
         testin.unread(ch);
    else if ((ch = pis.read()) != 0xBB){
         pis.unread(ch);
         pis.unread(
    0xef);
    else if ((ch = pis.read()) != 0xBF){
         
    throw new IOException("wrong format");
    else
    {
    }


    (3).InputStream读取完文件,干掉BOM头

    FileInputStream fin = new FileInputStream(fileName);
    //写入临时文件
    InputStream in = getInputStream(fin);
    String tmpFileName 
    = fileName + ".tmp";
    FileOutputStream out 
    = new FileOutputStream(tmpFileName);
    byte b[] = new byte[4096];

    int len = 0;
    while (in.available() > 0){
         len 
    = in.read(b, 04096);
         out.write(b, 
    0, len);
    }
    in.close();
    fin.close();
    out.close();

    //临时文件写完,开始将临时文件写回本文件。
    in = new FileInputStream(tmpFileName);
    System.out.println(
    "[" + fileName + "]");
    out 
    = new FileOutputStream(fileName);

    while (in.available() > 0){
         len 
    = in.read(b, 04096);
         out.write(b, 
    0, len);
    }
    in.close();
    out.close();


    (3).非法XML字符串

    解析XML文件时出现非法字符的Exception(即使该字符位于CDATA段内):    org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0xb) was found in the CDATA section.
    原因在于:根据W3C标准,有一些字符不能出现在XML文件中:
        0x00 - 0x08
        0x0b - 0x0c
        0x0e - 0x1f
    解析XML时遇到这些字符就会出错

    解决方法:
    对有可能出问题的XML文件,进行字符过滤后再进行解析。

    public static String stripNonValidXMLChars(String str) {
      
    if (str == null || "".equals(str)) {
        
    return str;
      }
      
    return str.replaceAll("[\x00-\x08\x0b-\x0c\x0e-\x1f]""");
    }
  • 相关阅读:
    Dubbo 安装监控中心
    Docker安装RabbitMQ
    Python 3 字符串转MD5形式
    Ubuntu 安装jupyter notebook
    设计模式学习-合成模式
    ubuntu 下安装jupyter
    使用物理备份恢复数据库
    matplotlib 中的subplot的用法
    pandas中关于DataFrame的groupby方法,如何取出分组的ID(行名称)和ID对应的个数
    java编程中'为了性能'一些尽量做到的地方
  • 原文地址:https://www.cnblogs.com/ttjava/p/3643560.html
Copyright © 2020-2023  润新知