在Java应用程序中,读取XML文件的最佳/最简单方法是什么?

59

目前我们的Java应用程序使用一个以制表符分隔的*.cfg文件中保存的值。我们需要修改这个应用程序,使其现在使用一个XML文件。

读取这个文件中的值,最好/最简单的库是什么?

12个回答

57

当然,根据您的需求,有很多好的解决方案。如果只是配置问题,您可以查看Jakarta commons-configurationcommons-digester

您始终可以使用获取文档的标准JDK方法:

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;

[...]

File file = new File("some/path");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(file);

4
同意。这是我阅读小型XML文件(如配置文件)的首选无废话方式。结合XPath,使提取配置参数更加简单。 - Neil Coffey
15
你没有展示如何读取属性和上下文。:( - chrisapotek
将功能安全处理设置为true以避免XXE攻击:DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - myhouse

38

XML代码:

<?xml version="1.0"?>
<company>
    <staff id="1001">
        <firstname>yong</firstname>
        <lastname>mook kim</lastname>
        <nickname>mkyong</nickname>
        <salary>100000</salary>
    </staff>
    <staff id="2001">
        <firstname>low</firstname>
        <lastname>yin fong</lastname>
        <nickname>fong fong</nickname>
        <salary>200000</salary>
    </staff>
</company>

Java 代码:

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import java.io.File;

public class ReadXMLFile {

  public static void main(String argv[]) {
    try {
    File fXmlFile = new File("/Users/mkyong/staff.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("staff");
    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("Staff id : "
                               + eElement.getAttribute("id"));
            System.out.println("First Name : "
                               + eElement.getElementsByTagName("firstname")
                                 .item(0).getTextContent());
            System.out.println("Last Name : "
                               + eElement.getElementsByTagName("lastname")
                                 .item(0).getTextContent());
            System.out.println("Nick Name : "
                               + eElement.getElementsByTagName("nickname")
                                 .item(0).getTextContent());
            System.out.println("Salary : "
                               + eElement.getElementsByTagName("salary")
                                 .item(0).getTextContent());
        }
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
  }
}

输出:

----------------

Root element :company
----------------------------

Current Element :staff
Staff id : 1001
First Name : yong
Last Name : mook kim
Nick Name : mkyong
Salary : 100000

Current Element :staff
Staff id : 2001
First Name : low
Last Name : yin fong
Nick Name : fong fong
Salary : 200000

我建议您阅读这篇文章:Normalization in DOM parsing with java - how does it work?

示例源代码。


我认为这是最好的方法,你可以更好地控制转换过程以及如何构建对象。我曾尝试使用dom4j,但由于客户端发送了意外的命名空间,它失败了。之前有人告诉我我们可能会在XML请求中发送一些意外的字段或节点。 - Mubashar
2
+1 是因为它实际上展示了如何读取数据...而最佳答案没有。 - chrisapotek
下次我会使用JAXB。 - Ran Adler
1
我知道这很老旧,但是相比在C#中读取XML文件,这真的太痛苦了。这难道是在Java中读取XML数据的最佳方式吗? - Anonymous
这绝对不是最好的方式。 - Ran Adler
将特性“secure processing”设置为true以避免XXE攻击:DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); dbFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - myhouse

11
“什么是最好/最简单的库,用于从此文件中读取值?”
“既然你要求使用最简单的库,我觉得有必要提供一种与Guillaume的获得最高票答案不同的方法。(在其他答案中,sjbotha的JDOM提到了我建议的内容)。”
“我认为,在Java中处理XML时,使用标准JDK工具肯定不是最简单的方法,只有在某些情况下(例如由于某些原因无法使用第三方库)才是最佳方法。”
“相反,考虑使用一个好的XML库,比如XOM。以下是将XML文件读入nu.xom.Document对象的方法:”
import nu.xom.Builder;
import nu.xom.Document;
import java.io.File;

[...]

File file = new File("some/path");
Document document = new Builder().build(file);

因此,将文件读入org.w3c.dom.Document在“纯JDK”方法中也不是很复杂,因此这只是简单的一点。但是使用好的库的优势仅从这里开始!无论您使用XML做什么,当使用像XOM这样的库时,您经常会得到更简单的解决方案,并且需要维护自己的代码较少。例如,考虑thisthis,或thisthis,或包含XOM和W3C DOM示例的此帖子
其他人提供反驳观点(like these),以说明坚持使用Java的标准XML API可能是值得的 - 这些观点可能有一定的价值,至少在某些情况下,尽管就个人而言,我并不赞同所有这些观点。无论如何,在选择一种方法或另一种方法时,了解故事的两面都是好的。

(这篇答案是我对XOM的评估之一,它在我的寻找最佳Java XML库来替代dom4j的过程中是一个强有力的竞争者。)


2
哦,我同意那些指出XML可能并不是最佳选择的人,但为了回答问题,我假设XML确实被使用。 - Jonik

8

你为什么选择使用XML配置文件?我以前做过XML配置,但它们经常变成更多的麻烦。

我想真正的问题是,在你的情况下,是否使用类似Preferences API这样的东西会更好。

使用Preferences API而不是自己编写XML解决方案的原因:

  • 避免典型的XML丑陋(DocumentFactory等),以及避免第三方库提供XML后端

  • 内置支持默认值(不需要特殊处理缺少/损坏/无效条目)

  • 无需为XML存储对值进行清理(CDATA包装等)

  • 支持存储状态保证(无需不断将XML写入磁盘)

  • 可配置的存储后端(磁盘文件、LDAP等)

  • 免费多线程访问所有首选项


1
偏好设置API具有XML输出 ;) http://java.sun.com/javase/6/docs/api/java/util/prefs/Preferences.html - Harald Schilly

4

JAXB 是易于使用的,且包含在Java 6 SE中。通过使用JAXB或其他XML数据绑定库,如Simple,您不必自己处理XML,大部分工作都由库完成。基本用法是向现有POJO添加注释。这些注释将用于为您的数据生成XML模式,并在从/向文件读取/写入数据时使用。


3

我只用过jdom,它非常容易上手。

这里提供文档和下载链接:http://www.jdom.org/

如果你有一个非常大的文档,最好不要把全部内容读入内存,而是使用SAX解析器,在遇到特定标签和属性时调用你的方法。你需要创建一个状态机来处理接收到的调用。


3

了解一下JAXB


1

根据您的应用程序和cfg文件的范围,属性文件可能是最简单的选择。虽然它不像XML那样优雅,但肯定更容易。


1

使用{{link1:java.beans.XMLDecoder}},自Java SE 1.4以来成为核心组件。

XMLDecoder input = new XMLDecoder(new FileInputStream("some/path.xml"));
MyConfig config = (MyConfig) input.readObject();
input.close();

手动编写配置文件非常简单,或者可以使用相应的XMLEncoder设置一些内容以在运行时编写新对象。


1
最简单的方法是使用Simple http://simple.sourceforge.net,你只需要像这样注释一个对象即可。
@Root
public class Entry {

   @Attribute
   private String a
   @Attribute
   private int b;
   @Element
   private Date c;

   public String getSomething() {
      return a;
   }
} 

@Root
public class Configuration {

   @ElementList(inline=true)
   private List<Entry> entries;

   public List<Entry> getEntries() { 
      return entries;
   }
}

然后,您只需指定位置即可读取整个文件,它将解析并填充带注释的POJO。这将执行所有类型转换和验证。如果需要,还可以为持久性回调进行注释。可以像下面这样进行阅读。

Serializer serializer = new Persister();
Configuration configuraiton = serializer.read(Configuration.class, fileLocation);

使用XStream,您无需注释任何内容... - willcodejavaforfood
要定制XML模式,您需要描述它,注释是完全有效的手段。JAXB,C# XML序列化甚至XStream都利用注释来实现这一点。我们在这里不谈论序列化,而是谈论映射到对象的结构化XML模式。 - ng.

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