【问题标题】:JAVA SAX parser split calls to characters()JAVA SAX 解析器拆分对 characters() 的调用
【发布时间】:2011-06-01 20:15:51
【问题描述】:

我正在做一个项目来解析 XML 中的一些数据。

例如,XML 是

<abc>abcdefghijklmno</abc>

我需要解析“abcdefghijkmnlp”。

但是当我测试我的解析时,我发现了一个大问题:

public class parser{
    private boolean hasABC = false;


        //Constructor HERE
        ......................
        ......................

     @Override
     public void startDocument () throws SAXException{  
     }

     @Override
     public void endDocument () throws SAXException{  
     }

     @Override
     public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException{  
          if ("abc".equalsIgnoreCase(localName)) {
              this.hasABC = true;
          }
      }
      @Override
      public void endElement(String namespaceURI, String localName, String qName) throws SAXException{
            if ("abc".equalsIgnoreCase(localName)) {
                 this.hasABC = false;
            }
       }
       @Override
       public void characters(char ch[], int start, int length){
            String content = new String(ch, start, length).trim(); 
            if(this.hasABC){
                 System.out.println("ABC = " + content);
            }
        }
    }

我发现解析器已经对标签进行了两次解析 系统打印出来的是,

ABC = abcdefghi

ABC = jklmno

为什么解析器会自动回调 characters() 两次????

XML 是否有一些“\n”或“\r”???

【问题讨论】:

    标签: java xml


    【解决方案1】:

    Parser 不止一次调用characters 方法,因为它可以并且根据规范允许。这有助于快速解析器并保持低内存占用。如果您想要单个字符串,请在 startElement 中创建一个新的 StringBuilder 对象并在 endElement 方法上对其进行处理。

    【讨论】:

    • 是的,我正在使用 gobal 变量将文本存储在 charaters() 中,并在 endElement() 中打印出这个变量。
    • @rebecca 问题中的代码 sn-p 没有这样做,我假设您现在指的是一些新修复的代码? :)
    【解决方案2】:

    您会感到惊讶,但这是记录在案的行为,即您不能假设解析器将在单个回调中读取并返回元素的所有文本数据。我之前也有过同样的经历。您需要编写代码来处理这种情况,或者您可以切换到Stax parser。您可以使用CharArrayWriter 跨多个回调累积数据。

    见下方JavaDoc of ContentHandler.characters(...)

    解析器会调用这个方法来 报告每一块字符数据。 SAX 解析器可能返回所有连续的 单个块中的字符数据,或 他们可以将其拆分成几个块; 但是,任何字符中的所有字符 单个事件必须来自同一个 外部实体,以便定位器 提供有用的信息。

    【讨论】:

    • 我真的希望有一些标志这样做:|
    • Or you can switch to Stax parser:我认为这是错误的。 StAX 也在拆分字符流。
    • @malat 使用 StAX,如果您使用 XMLStreamReader#getElementText(),则无需担心文本拆分。此方法在内部附加 START_ELEMENT 和 END_ELEMENT 事件之间遇到的所有文本内容。
    【解决方案3】:

    这是 SAX 的一个特性。解析器可以拆分文本段,并根据需要多次调用您的characters 方法。

    这样做的原因是性能,SAX 优先考虑易用性。 SAX 可能已经用尽了它的内部缓冲区,因此为了避免复制它,它会将迄今为止的数据传递给您的代码。

    【讨论】:

      【解决方案4】:

      您可以更改开始、结束和字符方法,例如:

      • 添加“全局”内容变量
      • 然后在 start 方法中将其设为 null (content == null)
      • 在结束方法中,您可以 println 或将该内容字符串添加到某个对象
      • 在字符方法中你可以做 if/else:

        if (content == null)
        {
            content = new String(ch, start, length);
        } else {
            content += new String(ch, start, length);
        }
        

        残酷的方式(最好用 stringbuilder 来做)但是有效,并且“字符串”不再被拆分。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-28
        • 2015-02-15
        • 2017-08-19
        • 2012-01-25
        • 1970-01-01
        • 2010-09-22
        • 1970-01-01
        相关资源
        最近更新 更多