[英]Reading Multi-Level XML files in Java
直到最近,我的XML文件的標簽結構還相當簡單。 但是現在我有了帶有標簽的額外級別的標簽,並且解析XML變得更加復雜。
這是我的新XML文件的示例(我更改了標簽名稱以使其更易於理解):
<SchoolRoster>
<Student>
<name>John</name>
<age>14</age>
<course>
<math>A</math>
<english>B</english>
</course>
<course>
<government>A+</government>
</course>
</Student>
<Student>
<name>Tom</name>
<age>13</age>
<course>
<gym>A</gym>
<geography>incomplete</geography>
</course>
</Student>
</SchoolRoster>
上面的XML的重要特征是,我可以具有多個“課程”屬性,並且在其中可以具有任意命名的標簽作為其子級。 這些孩子可以有任意數量,我想將它們讀入“名稱”,“值”的HashMap中。
public static TreeMap getAllSchoolRosterInformation(String fileName) {
TreeMap SchoolRoster = new TreeMap();
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File file = new File(fileName);
if (file.exists()) {
Document doc = db.parse(file);
Element docEle = doc.getDocumentElement();
NodeList studentList = docEle.getElementsByTagName("Student");
if (studentList != null && studentList.getLength() > 0) {
for (int i = 0; i < studentList.getLength(); i++) {
Student aStudent = new Student();
Node node = studentList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element e = (Element) node;
NodeList nodeList = e.getElementsByTagName("name");
aStudent.setName(nodeList.item(0).getChildNodes().item(0).getNodeValue());
nodeList = e.getElementsByTagName("age");
aStudent.setAge(Integer.parseInt(nodeList.item(0).getChildNodes().item(0).getNodeValue()));
nodeList = e.getElementsByTagName("course");
if (nodeList != null && nodeList.getLength() > 0) {
Course[] courses = new Course[nodeList.getLength()];
for (int j = 0; j < nodeList.getLength(); j++) {
Course singleCourse = new Course();
HashMap classGrades = new HashMap();
NodeList CourseNodeList = nodeList.item(j).getChildNodes();
for (int k = 0; k < CourseNodeList.getLength(); k++) {
if (CourseNodeList.item(k).getNodeType() == Node.ELEMENT_NODE && CourseNodeList != null) {
classGrades.put(CourseNodeList.item(k).getNodeName(), CourseNodeList.item(k).getNodeValue());
}
}
singleCourse.setRewards(classGrades);
Courses[j] = singleCourse;
}
aStudent.setCourses(Courses);
}
}
SchoolRoster.put(aStudent.getName(), aStudent);
}
}
} else {
System.exit(1);
}
} catch (Exception e) {
System.out.println(e);
}
return SchoolRoster;
}
我遇到的問題是,他們沒有讓學生在“數學”中得到“ A”,而是在“數學”中得到了空。 (如果這篇文章太長,我可以嘗試找到縮短它的方法。)
如果這是我的項目,我將避免嘗試手動剖析HTML中的數據,而是讓Java通過使用JAXB為我完成數據。 我越用這個工具,我就越喜歡它。 我敦促您考慮嘗試這種方法,因為如果這樣做,將XML更改為Java對象所需要做的就是在Java類中添加適當的批注,然后再將XML解組。 使用的代碼會簡單得多,因此出錯的可能性也要小得多。
例如,以下代碼非常容易和整潔地將信息編組為XML:
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement
public class SchoolRoster {
@XmlElement(name = "student")
private List<Student> students = new ArrayList<Student>();
public SchoolRoster() {
}
public List<Student> getStudents() {
return students;
}
public void addStudent(Student student) {
students.add(student);
}
public static void main(String[] args) {
Student john = new Student("John", 14);
john.addCourse(new Course("math", "A"));
john.addCourse(new Course("english", "B"));
Student tom = new Student("Tom", 13);
tom.addCourse(new Course("gym", "A"));
tom.addCourse(new Course("geography", "incomplete"));
SchoolRoster roster = new SchoolRoster();
roster.addStudent(tom);
roster.addStudent(john);
try {
JAXBContext context = JAXBContext.newInstance(SchoolRoster.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
String pathname = "MySchoolRoster.xml";
File rosterFile = new File(pathname );
marshaller.marshal(roster, rosterFile);
marshaller.marshal(roster, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
@XmlRootElement
@XmlType(propOrder = { "name", "age", "courses" })
class Student {
// TODO: completion left as an exercise for the original poster
}
@XmlRootElement
@XmlType(propOrder = { "name", "grade" })
class Course {
// TODO: completion left as an exercise for the original poster
}
這產生了以下XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<schoolRoster>
<student>
<name>Tom</name>
<age>13</age>
<courses>
<course>
<name>gym</name>
<grade>A</grade>
</course>
<course>
<name>geography</name>
<grade>incomplete</grade>
</course>
</courses>
</student>
<student>
<name>John</name>
<age>14</age>
<courses>
<course>
<name>math</name>
<grade>A</grade>
</course>
<course>
<name>english</name>
<grade>B</grade>
</course>
</courses>
</student>
</schoolRoster>
要將其解編到一個充滿數據的SchoolRoster類中,只需幾行代碼。
private static void unmarshallTest() {
try {
JAXBContext context = JAXBContext.newInstance(SchoolRoster.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String pathname = "MySchoolRoster.xml"; // whatever the file name should be
File rosterFile = new File(pathname );
SchoolRoster roster = (SchoolRoster) unmarshaller.unmarshal(rosterFile);
System.out.println(roster);
} catch (JAXBException e) {
e.printStackTrace();
}
}
在將toString()
方法添加到我的類之后,結果是:
SchoolRoster
[students=
[Student [name=Tom, age=13, courses=[Course [name=gym, grade=A], Course [name=geography, grade=incomplete]]],
Student [name=John, age=14, courses=[Course [name=math, grade=A], Course [name=english, grade=B]]]]]
for (int k = 0; k < CourseNodeList.getLength(); k++) {
if (CourseNodeList.item(k).getNodeType() == Node.ELEMENT_NODE && CourseNodeList != null) {
classGrades.put(CourseNodeList.item(k).getNodeName(),
CourseNodeList.item(k).getNodeValue());
}
}
您正在對Element
調用getNodeValue()
。 根據JDK API文檔,該方法返回null。
http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Node.html
您需要獲取子Text節點並在其上調用getNodeValue()
。 這是一種非常快捷而骯臟的方法:
classGrades.put(CourseNodeList.item(k).getNodeName(),
CourseNodeList.item(k).getChildNodes().item(0).getNodeValue());
請不要在生產代碼中使用此代碼。 它很丑。 但這會為您指明正確的方向。
像@Hovercraft一樣,我建議使用庫來處理到xml的序列化。 我發現Xstream具有出色的性能並且易於使用。 http://x-stream.github.io/
例如:
public static void saveStudentsXML(FileOutputStream file) throws Exception {
if (xstream == null)
initXstream();
xstream.toXML(proctorDAO.studentList, file);
file.close();
}
public static void initXstream() {
xstream = new XStream();
xstream.alias("student", Student.class);
xstream.useAttributeFor(Student.class, "lastName");
xstream.useAttributeFor(Student.class, "firstName");
xstream.useAttributeFor(Student.class, "id");
xstream.useAttributeFor(Student.class, "gradYear");
xstream.aliasAttribute(Student.class, "lastName", "last");
xstream.aliasAttribute(Student.class, "gradYear", "gc");
xstream.aliasAttribute(Student.class, "firstName", "first");
}
演示嵌套屬性的示例XML:
<list>
<student first="Ralf" last="Adams" gc="2014" id="100">
<testingMods value="1" boolMod="2"/>
</student>
<student first="Mick" last="Agosti" gc="2014" id="102">
<testingMods value="1" boolMod="2"/>
</student>
<student first="Edmund" last="Baggio" gc="2013" id="302">
<testingMods value="1" boolMod="6"/>
</student>
</list>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.