1

我阅读了JavaDOMParserSAXParserJava。我对 DOMParser 毫无疑问,人们更喜欢 SAXParser 而不是 DOMParser,因为它需要内存。但是我理解 SAXParser 的概念,我无法在这段代码下:

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class ReadXMLFileSAX {

 public static void main(String args[]) {

  try {

     SAXParserFactory factory = SAXParserFactory.newInstance();
     SAXParser saxParser = factory.newSAXParser();

     DefaultHandler handler = new DefaultHandler() {

     boolean bfname = false;
     boolean blname = false;
     boolean bnname = false;
     boolean bsalary = false;

     public void startElement(String uri, String localName,
        String qName, Attributes attributes)
        throws SAXException {

        System.out.println("Start Element :" + qName);

        if (qName.equalsIgnoreCase("FIRSTNAME")) {
           bfname = true;
        }

        if (qName.equalsIgnoreCase("LASTNAME")) {
           blname = true;
        }

        if (qName.equalsIgnoreCase("NICKNAME")) {
           bnname = true;
        }

        if (qName.equalsIgnoreCase("SALARY")) {
           bsalary = true;
        }

     }

     public void endElement(String uri, String localName,
          String qName)
          throws SAXException {

          System.out.println("End Element :" + qName);

     }

     public void characters(char ch[], int start, int length)
         throws SAXException {

         if (bfname) {
            System.out.println("First Name : "
                + new String(ch, start, length));
            bfname = false;
          }

          if (blname) {
              System.out.println("Last Name : "
                  + new String(ch, start, length));
              blname = false;
           }

          if (bnname) {
              System.out.println("Nick Name : "
                  + new String(ch, start, length));
              bnname = false;
           }

          if (bsalary) {
              System.out.println("Salary : "
                  + new String(ch, start, length));
              bsalary = false;
           }

        }

      };

      saxParser.parse("/home/anto/Groovy/Java/file.xml", handler);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

.xml 文件是:

<?xml version="1.0"?>
<company>
    <staff>
        <firstname>yong</firstname>
        <lastname>mook kim</lastname>
        <nickname>mkyong</nickname>
        <salary>100000</salary>
    </staff>
    <staff>
        <firstname>low</firstname>
        <lastname>yin fong</lastname>
        <nickname>fong fong</nickname>
        <salary>200000</salary>
    </staff>
</company>

当我运行程序时,我得到如下输出:

Start Element :company
Start Element :staff
Start Element :firstname
First Name : yong
End Element :firstname
Start Element :lastname
Last Name : mook kim
End Element :lastname
Start Element :nickname
Nick Name : mkyong
End Element :nickname
Start Element :salary
Salary : 100000
End Element :salary
End Element :staff
Start Element :staff
Start Element :firstname
First Name : low
End Element :firstname
Start Element :lastname
Last Name : yin fong
End Element :lastname
Start Element :nickname
Nick Name : fong fong
End Element :nickname
Start Element :salary
Salary : 200000
End Element :salary
End Element :staff
End Element :company

输出看起来很好,但我对输出感到困惑!输出的顺序是如何打印的?哪个处理这个?由于这是我第一次阅读 SAX 和 DOM,我无法理解,请帮助我。

4

7 回答 7

2

SAX 是基于事件的。因此,每次它看到一个开始标签、属性、标签中的字符、结束标签……它都会调用处理程序的适当函数。

所以这里的流程是:

  1. 看到company标签,打电话startElement给它
  2. 看到staff标签,打电话startElement给它
  3. 查看firstname标签,调用startElement它(设置布尔值)
  4. 查看字符(“yong”),characters为它们调用函数(查看设置了哪个布尔值并打印适当的消息并清除标志)
  5. 查看结束firstname标签,调用endElement函数

...

于 2011-04-04T15:57:03.297 回答
2

通过调用saxParser.parse("/home/anto/Groovy/Java/file.xml", handler);,SAX 解析器使用您实现的DefaultHandlerhandler即您作为参数传递的)来进行 XML 解析。

SAX 是基于事件的,当解析器遍历您的 XML 文档时会遇到这些事件。当 SAX 解析器遇到一个元素的开始时,例如<firstname>,它调用该startElement方法。然后,它遍历开始元素的主体,并看到yong. 由于它没有包含在<>标签中,因此它被视为文本节点,因此它调用该characters方法。如果有另一个 XML 元素,它将startElement再次调用新的 XML 元素。

最后,SAX 解析器遍历直到它看到结束元素</firstname>并调用该endElement方法。

所有这 3 种方法startElementcharacters并由endElement开发人员(在您的情况下为您)实现。

不要忘记,SAX 只遍历您的 XML 文档。它不记录哪个节点是哪个节点的父节点或子节点。

希望这可以帮助!

于 2011-04-04T16:00:32.097 回答
0

SAX 解析器的强大之处在于它的事件。您需要做的就是覆盖/实现正确的方法,并且解析库有责任按顺序调用事件。

于 2011-04-04T15:54:31.863 回答
0

订单在我看来很好。有什么问题?

如果您在谈论开始和结束元素,那只是显示了 XML 标记嵌套。您会看到“公司”在“员工”之前,“员工”在“名字”之前。

最后,您在各个标签内拥有数据本身。这就是为什么最后三行是:

End Element :salary
End Element :staff
End Element :company

因为是离开工资,工资是员工的最后一个要素,也是公司最后的员工。

于 2011-04-04T15:56:28.367 回答
0

当解析器读取输入 XML 时,它会调用startElement每个开始标记,并调用endElement每个结束标记。如果解析器遇到标签的内容,比如yong,它会调用characters.

bfname您发布的代码使用状态变量,bsalary等跟踪当前正在解析的标签。一旦characters被调用,您的代码就知道它被调用的是哪个实体——名字、姓氏或薪水,因此它可以正确地破译原始字符串。

因此,在编写 SAX 解析器时,实际上是在编写回调来跟踪 XML 中解析器的状态——它当前读取的是 XML 的哪一部分。

相反,在使用 DOM 解析器时,您会将整个 XML 文档转换为树,因此您可以以任何您喜欢的方式从它的根导航到节点,或向后导航——从节点到根。

于 2011-04-04T15:57:39.443 回答
0

SAX 解析器只遍历一个文档,一次一个字符。Parser的parse()方法接受一个Handler对象。当解析器遇到文档中的某些字符(“事件”)时,解析器会调用此对象的各种方法。所以解析器每次遇到开始标签就调用startElementHandler的方法,遇到结束标签就调用endElement方法,以此类推。DefaultHandler 中的这些方法是空的。您可以对此类进行子类化并提供您自己的这些方法的实现(在您上面的代码示例中,Defaulthandler 已被匿名子类化)。

与 DOM 解析器不同,SAX 解析器不构造元素——它只是在遇到开始和结束标记以及内容字符时触发各种处理程序方法。您可以在这些方法中提供将结束标记映射到开始标记等的逻辑,这就是条件语句在 startElement 和 endElement 方法中所做的事情。并且类变量blname等只是跟踪解析器当前所在的元素 - 以便您知道与传递到characters()方法中的字符相关的内容。

于 2011-04-04T15:59:00.453 回答
0

接近尾声时,您会注意到该saxParser.parse()方法是handler作为参数给出的。DefaultHandler处理程序是前面在代码中定义的一个实例。SAXParser 在解析 XML 文档时调用处理程序上的适当方法。这是关于DefaultHandlerSAXParser的一些 Javadoc (请参阅有关parse方法的文档)。在解析 XML 文档并依次调用处理程序中的每个方法时,处理程序方法打印出已处理的值。

于 2011-04-04T15:59:04.900 回答