用 Java 读取多级 XML 文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11268762/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 04:27:58  来源:igfitidea点击:

Reading Multi-Level XML files in Java

javaxml

提问by theJollySin

Until recently, the tag-structure of my XML files were fairly simple. But now I have an extra level of tags with tags, and parsing the XML has become more complicated.

直到最近,我的 XML 文件的标记结构还相当简单。但现在我多了一层带标签的标签,解析 XML 变得更加复杂。

Here is an example of my new XML files (I've changed the tag names to make it easier to understand):

这是我的新 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>

The important features of the XML above is that I can have multiple "course" attributes, and inside that I can have arbitrarily named tags as their children. And there can be any number of these children, which I want to read into a HashMap of "name", "value".

上面 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;
}

The problem I'm running in to is that instead of getting that the student got an "A" in "math", they get a null in "math". (If this post is too long, I can try to find some way of shortening it.)

我遇到的问题是,不是让学生在“数学”中得到“A”,而是在“数学”中得到一个空值。(如果这篇文章太长,我可以尝试找到一些缩短它的方法。)

回答by Hovercraft Full Of Eels

If this were my project I would avoid trying to manually dissect the data in the HTML and instead let Java do it for me by using JAXB. The more I use this tool, the more I love it. I urge you to consider trying this, for if you do, all you would need to change the XML into Java objects is proper annotations in your Java classes and then unmarshall the XML. The code used would be much simpler and thus much less likely to have errors.

如果这是我的项目,我将避免尝试手动剖析 HTML 中的数据,而是让 Java 使用 JAXB 为我完成。我越是使用这个工具,我就越喜欢它。我强烈建议您考虑尝试这样做,因为如果您这样做了,将 XML 更改为 Java 对象所需的全部工作就是在 Java 类中正确注释,然后解组 XML。使用的代码会简单得多,因此出错的可能性也小得多。

For instance, the following code marshalled information into an XML quite easily and cleanly:

例如,以下代码非常容易和干净地将信息编组到 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
}

This resulted in the following XML:

这导致了以下 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>

To unmarshall this into a SchoolRoster class filled with data would only take a few lines of code.

将其解组为一个充满数据的 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();
  }
}

After adding toString()methods to my classes, this resulted in:

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]]]]]

回答by Dan O

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());
    }
}

You're calling getNodeValue()on an Element. According to the JDK API docs, that returns null.

你调用getNodeValue()Element。根据 JDK API 文档,它返回 null。

http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Node.html

http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/Node.html

You need to get the child Text node and call getNodeValue()on that. Here's a really quick and dirty way to do so:

您需要获取子 Text 节点并调用getNodeValue()它。这是一个非常快速和肮脏的方法:

classGrades.put(CourseNodeList.item(k).getNodeName(),
        CourseNodeList.item(k).getChildNodes().item(0).getNodeValue());

Please don't use this in production code. It's ugly. But it'll point you in the right direction.

请不要在生产代码中使用它。它很丑。但它会为你指明正确的方向。

回答by Thorn

Like @Hovercraft I recommend using a library to handles the serialization to xml. I found Xstream to be have great performance and easy to use. http://x-stream.github.io/

像@Hovercraft 一样,我建议使用一个库来处理 xml 的序列化。我发现 Xstream 具有出色的性能且易于使用。 http://x-stream.github.io/

For example:

例如:

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");
}

Sample XML to demonstrate a nested property:

演示嵌套属性的示例 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>