如何在startElement中使用SAX解析器从XML中获取元素的值?

8

你能否在SAX处理程序的覆盖函数 startElement 中从XML文件获取元素内容?

以下是规范。

1)XML文件

<employees>
   <employee id="111">
      <firstName>Rakesh</firstName>
      <lastName>Mishra</lastName>
      <location>Bangalore</location>
   </employee>
   <employee id="112">
      <firstName>John</firstName>
      <lastName>Davis</lastName>
      <location>Chennai</location>
   </employee>
   <employee id="113">
      <firstName>Rajesh</firstName>
      <lastName>Sharma</lastName>
      <location>Pune</location>
   </employee>
</employees>

2) startElement function

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    .......code in here..........
}

3) 预期结果

element name   : employee
attribute name : id
attribute value: 111
firstName      : Rakesh
lastName       : Mishra
location       : Bangalore

element name   : employee
attribute name : id
attribute value: 112
firstName      : John
lastName       : Davis
location       : Chennai

element name   : employee
attribute name : id
attribute value: 113
firstName      : Rajesh
lastName       : Sharma
location       : Pune

http://www.mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/ - user1801972
@PawanAryan,谢谢。我已经检查过了。如果我说我只想在startElement函数中编写代码,这是可能的吗? - sakura
2
你只能在 startElement 中获取属性。任何文本值都可以在 characters 中获取。你应该使用 startElement 来检测元素何时开始。在其中,您可以设置标志,然后在 characters 方法中进行检查。知道当前元素是哪个,在 characters 中就可以获取其值。您必须记得在 endElement 中重置这些标志。 - helderdarocha
1
使用startElement()和其他方法是访问XML数据的唯一方式。我认为不可能在startElement()中编写所有内容。SAX解析器与DOM不同,因为它不会将完整的XML加载到内存中并按顺序读取xml文档。startElement():每次SAX解析器获取一个开放标签'<'时,它都会调用startElement()。endElement():每次SAX解析器获取一个关闭标签'>'时,它都会调用endElement()。character():每次SAX解析器获取一个简单字符字符串时,它都会调用character()方法,并根据在startElement()中编写的代码处理xml。 - user1801972
@PawanAryan,感谢您提供易于理解的概念。 这个选项怎么样?我想要一组tagName、attName、attValue和tag的值。我之所以问这个问题,是因为我需要在另一个线程中使用它。 - sakura
显示剩余2条评论
1个回答

13

您可以在startElementendElement中获取元素的名称。您还可以在startElement中获取属性。应该在characters中获取值。

以下是使用ContentHandler获取元素值的非常基本的示例:

public class YourHandler extends DefaultHandler {

    boolean inFirstNameElement = false;

    public class startElement(....) {
        if(qName.equals("firstName") {
            inFirstNameElement = true;
        }
    }

    public class endElement(....) {
        if(qName.equals("firstName") {
            inFirstNameElement = false;
        }
    }

    public class characters(....) {
        if(inFirstNameElement) {
            // do something with the characters in the <firstName> element
        }
    }
}

如果您有一个简单的例子,为每个标签设置布尔标志是可以的。如果您有一个更复杂的场景,您可能更喜欢将标志存储在一个映射中,使用元素名称作为键,甚至创建一个或多个Employee类映射到您的XML,在每次在startElement中找到<employee>时实例化它们,填充其属性,并在endElement中添加到集合中。

这是一个完整的ContentHandler示例,可与您的示例文件一起使用。希望能帮助您入门:

public class SimpleHandler extends DefaultHandler {

    class Employee {
        public String firstName;
        public String lastName;
        public String location;
        public Map<String, String> attributes = new HashMap<>();
    }
    boolean isFirstName, isLastName, isLocation;
    Employee currentEmployee;
    List<Employee> employees = new ArrayList<>();

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes atts) throws SAXException {
        if(qName.equals("employee")) {
            currentEmployee = new Employee();
            for(int i = 0; i < atts.getLength(); i++) {
                currentEmployee.attributes.put(atts.getQName(i),atts.getValue(i));
            }
        }
        if(qName.equals("firstName")) { isFirstName = true; }
        if(qName.equals("lastName"))  { isLastName = true;  }
        if(qName.equals("location"))  { isLocation = true;  }
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if(qName.equals("employee")) {
            employees.add(currentEmployee);
            currentEmployee = null;
        }
        if(qName.equals("firstName")) { isFirstName = false; }
        if(qName.equals("lastName"))  { isLastName = false;  }
        if(qName.equals("location"))  { isLocation = false;  }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (isFirstName) {
            currentEmployee.firstName = new String(ch, start, length);
        }
        if (isLastName) {
            currentEmployee.lastName = new String(ch, start, length);
        }
        if (isLocation) {
            currentEmployee.location = new String(ch, start, length);
        }
    }

    @Override
    public void endDocument() throws SAXException {
        for(Employee e: employees) {
            System.out.println("Employee ID: " + e.attributes.get("id"));
            System.out.println("  First Name: " + e.firstName);
            System.out.println("  Last Name: " + e.lastName);
            System.out.println("  Location: " + e.location);
        }
    }
}

请问!!! 这个选项怎么样? 如果我们不知道tagName或其他什么,但是我们想一次性获取tagName、attName、attValue和tagValue。这可能吗? - sakura
如上所示,在startElement方法中,您可以读取标记名称(qName)和所有属性,您可以从atts变量中读取(atts.getQName(i)atts.getValue(i)),但要读取标记的文本值,您需要使用characters方法并像上面所示使用标志。如果您运行上面的示例,您应该会得到您期望的结果。 - helderdarocha
不同的xml文件怎么办?我们需要实现其他代码吗? 我注意到你的代码在条件中使用了特定的tagName。如果我们不知道具体的标签名,该怎么办? - sakura
如果您愿意,可以直接打印标签名称,而不是根据标签名称设置标志,可以根据它们的相对位置进行操作(创建一个Map并在阅读时存储名称和上下文)。 - helderdarocha
1
SAX旨在对XML文件进行顺序读取,这在需要从大文件中提取信息的情况下是必要的。如果您想通过使用提取所需数据的方法一次性获取所有数据,您可能更喜欢使用对象模型API,例如DOM或XPath。 - helderdarocha
1
请注意,“SAX解析器可能会将所有连续的字符数据返回为单个块,也可能将其分成几个块”。因此,您需要累积数据。 - Ludovic Kuty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接