使用 SAX 解析器,如何解析具有相同名称标签但元素不同的 xml 文件?
Posted
技术标签:
【中文标题】使用 SAX 解析器,如何解析具有相同名称标签但元素不同的 xml 文件?【英文标题】:using SAX parser, how do you parse an xml file which has same name tags but in different elements? 【发布时间】:2011-11-04 19:32:12 【问题描述】:是否可以在 SAX 解析器中给出路径表达式?我有一个 XML 文件,它有几个相同的名称标签,但它们在不同的元素中。有什么方法可以区分它们。 这是 XML:
<Schools>
<School>
<ID>335823</ID>
<Name>Fairfax High School</Name>
<Student>
<ID>4195653</ID>
<Name>Will Turner</Name>
</Student>
<Student>
<ID>4195654</ID>
<Name>Bruce Paltrow</Name>
</Student>
<Student>
<ID>4195655</ID>
<Name>Santosh Gowswami</Name>
</Student>
</School>
<School>
<ID>335824</ID>
<Name>FallsChurch High School</Name>
<Student>
<ID>4153</ID>
<Name>John Singer</Name>
</Student>
<Student>
<ID>4154</ID>
<Name>Shane Warne</Name>
</Student>
<Student>
<ID>4155</ID>
<Name>Eddie Diaz</Name>
</Student>
</School>
</Schools>
我想将学生的姓名和 ID 与学校的名称和 ID 区分开来。
感谢您的回复:
我创建了一个学生 pojo,它具有以下字段 - school_id、school_name、student_id 和 student_name 以及它们的 getter 和 setter 方法。这是我的临时解析器实现。当我解析 xml 时,我需要将学校名称、id、学生姓名、id 的值放入 pojo 中并返回。你能告诉我我应该如何实现差异化堆栈。这是我的解析器框架::
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class HandleXML extends DefaultHandler
private student info;
private boolean school_id = false;
private boolean school_name = false;
private boolean student_id = false;
private boolean student_name = false;
private boolean student = false;
private boolean school = false;
public HandleXML(student record)
super();
this.info = record;
school_id = false;
school_name = false;
student_id = false;
student_name = false;
student = false;
school = false;
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes)
throws SAXException
if (qName.equalsIgnoreCase("student"))
student = true;
if (qName.equalsIgnoreCase("school"))
school_id = true;
if (qName.equalsIgnoreCase("school_id"))
school_id = true;
if (qName.equalsIgnoreCase("student_id"))
student_id = true;
if (qName.equalsIgnoreCase("school_name"))
school_name = true;
if (qName.equalsIgnoreCase("student_name"))
student_name = true;
@Override
public void endElement(String uri, String localName,
String qName)
throws SAXException
@Override
public void characters(char ch[], int start, int length)
throws SAXException
String data = new String(ch, start, length);
【问题讨论】:
另请查看:***.com/questions/1863250 - 有些项目允许您将 XPath 的子集与流式文档一起使用。如果您可以将您的问题放入该子集中,则生成的代码将比任何手动 SAX 处理程序代码更可取。 带上下文的 SAX 解析就像一个状态机:en.wikipedia.org/wiki/Finite-state_machine,你需要有一些可以打开/关闭的标志来知道你在哪里,但它很快就会变得非常混乱,你在你走得太远之前应该考虑替代方案。 【参考方案1】:在 SAX 解析器中,您将按文档顺序获得每个元素。您必须维护一个堆栈来跟踪嵌套(在处理 startElement 时压入堆栈,并为 endElement 弹出)。您可以通过当前堆栈中的内容来区分不同的 <Name>
元素。
或者,只保留一个变量来告诉您是否遇到了<School>
标记或<Student>
标记来告诉您您看到的是哪种类型的<Name>
。
【讨论】:
+1 用于保持堆栈,这就是要走的路。您可以通过打印堆栈的当前内容来生成类似 Xpath 的字符串。使用标志来判断你所在的标签是丑陋的。 :-P【参考方案2】:嗯,我已经好几年没有在 Java 中使用 SAX 了,所以这是我的看法:
package play.xml.sax;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class Test1
public static void main(String[] args)
SAXParserFactory spf = SAXParserFactory.newInstance();
SchoolsHandler handler = new SchoolsHandler();
try
SAXParser sp = spf.newSAXParser();
sp.parse("schools.xml", handler);
System.out.println("Number of read schools: " + handler.getSchools().size());
catch (SAXException se)
se.printStackTrace();
catch (ParserConfigurationException pce)
pce.printStackTrace();
catch (IOException ie)
ie.printStackTrace();
class SchoolsHandler extends DefaultHandler
private static final String TAG_SCHOOLS = "Schools";
private static final String TAG_SCHOOL = "School";
private static final String TAG_STUDENT = "Student";
private static final String TAG_ID = "ID";
private static final String TAG_NAME = "Name";
private final Stack<String> tagsStack = new Stack<String>();
private final StringBuilder tempVal = new StringBuilder();
private List<School> schools;
private School school;
private Student student;
public void startElement(String uri, String localName, String qName, Attributes attributes)
pushTag(qName);
tempVal.setLength(0);
if (TAG_SCHOOLS.equalsIgnoreCase(qName))
schools = new ArrayList<School>();
else if (TAG_SCHOOL.equalsIgnoreCase(qName))
school = new School();
else if (TAG_STUDENT.equalsIgnoreCase(qName))
student = new Student();
public void characters(char ch[], int start, int length)
tempVal.append(ch, start, length);
public void endElement(String uri, String localName, String qName)
String tag = peekTag();
if (!qName.equals(tag))
throw new InternalError();
popTag();
String parentTag = peekTag();
if (TAG_ID.equalsIgnoreCase(tag))
int id = Integer.valueOf(tempVal.toString().trim());
if (TAG_STUDENT.equalsIgnoreCase(parentTag))
student.setId(id);
else if (TAG_SCHOOL.equalsIgnoreCase(parentTag))
school.setId(id);
else if (TAG_NAME.equalsIgnoreCase(tag))
String name = tempVal.toString().trim();
if (TAG_STUDENT.equalsIgnoreCase(parentTag))
student.setName(name);
else if (TAG_SCHOOL.equalsIgnoreCase(parentTag))
school.setName(name);
else if (TAG_STUDENT.equalsIgnoreCase(tag))
school.addStudent(student);
else if (TAG_SCHOOL.equalsIgnoreCase(tag))
schools.add(school);
public void startDocument()
pushTag("");
public List<School> getSchools()
return schools;
private void pushTag(String tag)
tagsStack.push(tag);
private String popTag()
return tagsStack.pop();
private String peekTag()
return tagsStack.peek();
class School
private int id;
private String name;
private List<Student> students = new ArrayList<Student>();
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getId()
return id;
public void setId(int id)
this.id = id;
public void addStudent(Student student)
students.add(student);
public List<Student> getStudents()
return students;
class Student
private int id;
private String name;
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getId()
return id;
public void setId(int id)
this.id = id;
schools.xml
包含您的示例 XML。请注意,我将所有内容都塞进了一个文件中,但这只是因为我只是在玩耍。
【讨论】:
【参考方案3】:是的,使用 SAX 解析器理解 xml 通常比使用 DOM 复杂一些。基本上,您需要在 SAX 解析器中维护状态/上下文,以便区分这些情况。
注意,实现 SAX 处理程序的另一个关键是了解值可能会在多个字符事件中拆分。
【讨论】:
【参考方案4】:Sax 是基于事件的,通过回调您可以连续读取 XML 文档。 Sax 非常适合读取大型 XML 文档,因为整个文档不会加载到内存中。你可能想看看Xpath,例如
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
String expression = "/Schools/school/ ...";
XPathExpression xPathExpression = xPath.compile(expression);
// Compile the expression to get a XPathExpression object.
Object result = xPathExpression.evaluate(xmlDocument);
【讨论】:
这在 SAX 中是可能的,正如 Jim Garrison 的回答中所解释的那样。 @Don Roby,可以进行更正,但您真的会用 SAX 这样做吗?我认为以这种方式使用 SAX 过于复杂,可以使用 XPath 以更简洁的方式实现(没有标志,没有堆栈),但这只是我的看法 编写 SAX 应用程序需要更多的工作,但如果您需要 SAX 提供的内存节省并且能够承担额外的编程工作来实现它,那么它是完全合法的。但是,我认为您的担心是对的:需要提出此线程中提出的问题的人在使用 SAX 时会遇到很多困难。 @Michael Kay 唷有人同意 :) 其他人否决了我 :( 好吧,您的开场白是“萨克斯不可能做到这一点”。无论您是否认为这是最好的解决方案,这肯定是可能的。【参考方案5】:private boolean isInStudentNode;
...................................................
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
// enter node Student
if(qName.equalEgnoreCase("Student")
isInStudentNode = true;
...
public void endElement(String uri, String localName, String qName) throws SAXException
// end node Student
if(qName.equalEgnoreCase("Student")
isInStudentNode = false;
...........
// end node Name (school|student)
if(qName.equalEgnoreCase("Name")
if(isInStudentNode) student.setName(...);
else school.setName(...);
它和我一起工作
【讨论】:
以上是关于使用 SAX 解析器,如何解析具有相同名称标签但元素不同的 xml 文件?的主要内容,如果未能解决你的问题,请参考以下文章