【问题标题】:How to read XML file using java如何使用java读取XML文件
【发布时间】:2022-01-17 16:42:35
【问题描述】:

我正在尝试从 XML 文件中读取一些数据并且遇到了一些问题,我拥有的 XML 如下:

 <Tree>
  <child>
   <Property Name="id"/>
   <Property Name="username">abc</Property>
   <Property Name="phoneType">phone1</Property>
   <Property Name="value">123456</Property>
   </child>
   <child>
   <Property Name="id"/>
   <Property Name="username">def</Property>
   <Property Name="phoneType">phone2</Property>
   <Property Name="value">6789012</Property>
   </child>
   </Tree>

我正在尝试将这些值作为字符串读取到我的 Java 程序中,到目前为止我已经编写了以下代码:

File fXmlFile = new File("C:\\Users\\welcome\\Downloads\\ta\\abc.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fXmlFile);
doc.getDocumentElement().normalize();

System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
NodeList nList = doc.getElementsByTagName("child");
System.out.println("----------------------------");

for (int temp = 0; temp < nList.getLength(); temp++) {
    Node nNode = nList.item(temp);
    System.out.println("\nCurrent Element :" + nNode.getNodeName());
    if (nNode.getNodeType() == Node.ELEMENT_NODE) {
        Element eElement = (Element) nNode;
        System.out.println("id id : "
                           + eElement.getAttribute("id"));

我正在努力读取和打印 id、用户名等的值。

【问题讨论】:

  • NodeList nList = doc.getElementsByTagName("Object") - 你没有任何称为Object的元素...它们被称为Property。同样,您没有任何名为id 的属性。您文档中的唯一属性称为Name
  • getAttribute("id") 不正确。您的属性是 Name,其值为 id。您还跳过了子节点。不确定这是不是故意的
  • @OneCricketeer:如何从 def 打印 def
  • 我不知道。 eElement.getNodeValue() 怎么样?

标签: java xml


【解决方案1】:

我建议您使用像 jsoup 这样的库来读取 XML 文件,因为您可以获得很多开箱即用的功能。

另请阅读:How to parse XML with jsoup

【讨论】:

    【解决方案2】:

    您可以使用XPath 运行完全不同的查询。

    File fXmlFile = new File("C:\\Users\\welcome\\Downloads\\ta\\abc.xml");
    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
    Document doc = dBuilder.parse(fXmlFile);
    
    //Get an XPath object and evaluate the expression
    XPath xpath = XPathFactory.newInstance().newXPath();
    String propertyId = xpath.evaluate("/Tree/child[1]/Property[@name='id']", document);
    

    您更有可能想要遍历所有子元素,可以这样做

    NodeList children = (NodeList)xpath.evaluate("/Tree/child", doc, XPathConstants.NODESET);
    for (int i=0;i<children.getLength();i++) {
        Element child = (Element)children.get(i);
        String propertyId = child.getAttribute("id");
        ...
    }
    

    【讨论】:

      【解决方案3】:

      无论如何,读取 XML 文件都不是一件容易的事。它要求读者对文件的结构有深入的了解。我的意思是,元素名称是什么,属性名称,属性的数据类型,元素的顺序,元素是简单还是复杂(意味着它们是扁平的或下面有嵌套元素)。

      如 Jon Skeet 的评论所示,一种解决方案是使用 Java Document API。该接口具有从 XML 文件获取数据所需的所有方法。但是,在我看来,这仍然留给读者了解元素和属性名称的任务。

      如果给定 XML 的 XML 模式 (XSD) 或文档类型定义 (DTD) 可用或可以轻松构建,我更喜欢使用众多库之一来解析 XML 内容;仅举几例 StaX、JDOM、DOM4j、JAXB。因为我已经广泛使用它,所以我更喜欢 JAXB。 JAXB 有一些限制,但这些限制超出了本讨论的范围。值得一提的是,JAXB 包含在 Java 6 到 10 的 Java 发行版中。在这些版本之外,您必须自己下载 JAXB 发行版。

      我使用 JAXB 的主要原因之一是我可以在 POJO 中使用注释来根据现有 XML 构造一个类,而无需构建模式。当然,这并不总是那么简单。它几乎总是根据模式编译您的 JAXB 类。因为这将为您的 XML 文档生成 Java 自定义类,所以您可以通过其 getter 方法将元素称为属性,而不是让读者知道元素名称的负担。

      我使用 OPs XML 文件使用XML Copy Editor 生成架构。生成的架构如下所示:

      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
        <xs:element name="Tree">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="child" maxOccurs="unbounded"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="child">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="Property" maxOccurs="unbounded"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="Property">
          <xs:complexType mixed="true">
            <xs:attribute name="Name" type="xs:string" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:schema>
      

      一旦你有了模式,你就可以使用它来编译 JAXB 类,使用 Java 附带的 XJC 编译器。下面是一个关于如何编译 JAXB 类的示例:https://docs.oracle.com/javase/tutorial/jaxb/intro/examples.html

      要下载 JAXB 编译器,请转到 https://javaee.github.io/jaxb-v2/ 并单击“下载独立发行版”。您可以将 ZIP 文件的内容放在计算机上的任何位置。然后,只需在环境变量上设置 JAXB_HOME 即可。这可能看起来像很多工作,但到目前为止,这些都是一次性的活动。好处是,当您设置好环境后,编译所有类实际上会花费您几秒钟的时间;即使您需要基于 XML 生成架构。

      执行编译器生成Tree.javaChild.javaProperty.java

      Tree.java

      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "", propOrder = {
          "child"
      })
      @XmlRootElement(name = "Tree")
      public class Tree {
      
          @XmlElement(required = true)
          protected List<Child> child;
      
          public List<Child> getChild() {
              if (child == null) {
                  child = new ArrayList<Child>();
              }
              return this.child;
          }
      }
      

      Child.java

      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "", propOrder = {
          "property"
      })
      @XmlRootElement(name = "child")
      public class Child {
      
          @XmlElement(name = "Property", required = true)
          protected List<Property> property;
      
          public List<Property> getProperty() {
              if (property == null) {
                  property = new ArrayList<Property>();
              }
              return this.property;
          }
      }
      

      Property.java

      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "", propOrder = {
          "content"
      })
      @XmlRootElement(name = "Property")
      public class Property {
      
          @XmlValue
          protected String content;
          @XmlAttribute(name = "Name", required = true)
          protected String name;
      
          public String getContent() {
              return content;
          }
      
          public void setContent(String value) {
              this.content = value;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String value) {
              this.name = value;
          }
      }
      

      如何使用这些类

      读取过程(解组)将 XML 文件转换为这些生成的数据类型。 JAXB 解组过程使用JAXBContext 实用程序类创建解组器,然后调用解组方法将 XML 文件转换为对象:

      JAXBContext context = JAXBContext.newInstance(Tree.class); // the argument is the root node
      Tree xmlDoc = (Tree) context.createUnmarshaller().unmarshal(new FileReader("abc.xml")); // Reads the XML and returns a Java object
      

      要编写,您将使用 Java 类来存储数据并创建结构。在这种情况下,您需要创建所需的Property 对象、属性元素的Child 容器以及作为Tree 节点的根节点。您可以一次添加一个元素,也可以创建一个列表并一次添加所有元素。填充根节点对象后,只需将其传递给封送器...

      JAXBContext context = JAXBContext.newInstance(Tree.class);
      Marshaller mar= context.createMarshaller();
      mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // formatting the xml file
      mar.marshal(tree, new File("abc.xml")); // saves the "Tree" object as "abc.xml"
      

      大家一起

      import java.io.File;
      import java.io.FileNotFoundException;
      import java.io.FileReader;
      import java.util.List;
      
      import javax.xml.bind.JAXBContext;
      import javax.xml.bind.JAXBException;
      import javax.xml.bind.Marshaller;
      
      public class JAXBDemo {
          public static void main(String[] args) {
              try {
                  // write
                  Tree tree = new Tree();
                  Property prop0 = new Property();
                  prop0.setName("id");
                  prop0.setContent("");
                  
                  Property prop1 = new Property();
                  prop1.setName("username");
                  prop1.setContent("abc");
                  
                  Property prop2 = new Property();
                  prop2.setName("phoneType");
                  prop2.setContent("phone1");
      
                  Property prop3 = new Property();
                  prop3.setName("value");
                  prop3.setContent("123456");
      
                  List<Property> props1 = List.of(prop0, prop1, prop2, prop3);
      
                  Property prop4 = new Property();
                  prop4.setName("id");
                  prop4.setContent("");
                  
                  Property prop5 = new Property();
                  prop5.setName("username");
                  prop5.setContent("def");
                  
                  Property prop6 = new Property();
                  prop6.setName("phoneType");
                  prop6.setContent("phone2");
      
                  Property prop7 = new Property();
                  prop7.setName("value");
                  prop7.setContent("6789012");
      
                  List<Property> props2 = List.of(prop4, prop5, prop6, prop7);
                  
                  Child child1 = new Child();
                  Child child2 = new Child();
                  
                  child1.getProperty().addAll(props1);
                  child2.getProperty().addAll(props2);
                  
                  tree.getChild().add(child1);
                  tree.getChild().add(child2);
      
                  JAXBContext context = JAXBContext.newInstance(Tree.class);
                  Marshaller mar= context.createMarshaller();
                  mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                  mar.marshal(tree, new File("abc.xml"));
      
                  // read
                  Tree xmlDoc = (Tree) context.createUnmarshaller().unmarshal(new FileReader("abc.xml"));
                  List<Child> children = xmlDoc.getChild();
                  int i = 1;
                  for (Child child : children) {
                      System.out.println("Property " + i++ + ":");
                      List<Property> props = child.getProperty();
                      for (Property prop : props) {
                          System.out.println("Name: " + prop.getName() + "; Content: " + prop.getContent());
                      }
                  }
              } catch (JAXBException | FileNotFoundException e) {
      
                  e.printStackTrace();
              }
          }
      }
      

      最后的笔记:

      为了让它工作,我必须对发行版进行一些“修复”。第一个修复是根据这篇文章编辑xjc.bathttps://github.com/eclipse-ee4j/jaxb-ri/issues/1321。滚动到底部以查看我应用的修复。

      然后,我需要将我的“jaxb-runtime”依赖项更新到 2.3.3 版,以便项目与“jaxb-api”2.3.1 版一起使用。

      【讨论】:

      • 您的序言不正确。读取 XML 只需要语法。您提到的是关于理解 XML 和早期检测错误的方法(语法、语义)。并非所有工具都需要这个。
      • @HiranChaudhuri 您的帖子本身就证明了我的观点。如果您不知道 1) 某个节点包含此类属性,以及 2) 您知道如何访问该元素,如何获取 id 属性的值?这需要对 XML 语法和结构有深入的了解。抱歉,您的评论有误。
      • 继续您的示例:并非所有工具都需要访问 id 属性。并不是所有的项目都足够大或足够严格来证明使用 XSD 和持久性框架是合理的。
      • @HiranChaudhuri 这两种说法都无关紧要。您仍然需要知道存在这样的Tree(根)节点,并且它的子节点是child 元素的列表。以此类推。
      • @HiranChaudhuri 无论团队的规模如何,XSD 或 DTD 都可以更轻松地使用 XML 文档,因为它们充当了消费者和生产者之间的契约。使用模式,您知道文档的结构和语法,但您也知道叶节点的数据类型和可能存在的任何约束(即哪些节点是可选的)。使用普通的 Document 阅读器很难完成这项任务。
      猜你喜欢
      • 2011-01-20
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      • 1970-01-01
      • 2011-09-06
      • 2011-02-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多