如何使用Java获取XML元素的值?

56

我是XML的新手。我想根据请求名称读取以下XML。请帮忙告诉我如何在Java中读取以下XML -

<?xml version="1.0"?>
    <config>
        <Request name="ValidateEmailRequest">
            <requestqueue>emailrequest</requestqueue>
            <responsequeue>emailresponse</responsequeue>
        </Request>
        <Request name="CleanEmail">
            <requestqueue>Cleanrequest</requestqueue>
            <responsequeue>Cleanresponse</responsequeue>
        </Request>
    </config>

1
这个问题非常类似于您在这里提出的另一个问题:http://stackoverflow.com/questions/4013687/how-to-append-a-new-value-in-xml-using-java/4013881#4013881。 - William
10个回答

76

如果你的XML是一个字符串,那么你可以这样做:

String xml = ""; //Populated XML String....

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(xml)));
Element rootElement = document.getDocumentElement();
如果你的XML在一个文件中,那么Document document将会像这样被实例化:
Document document = builder.parse(new File("file.xml"));
document.getDocumentElement()方法返回的是文档元素节点(在您的情况下为<config>)。一旦您有了一个rootElement,您可以通过调用rootElement.getAttribute()方法访问该元素的属性等。有关java的更多方法,请参阅org.w3c.dom.Element。有关java DocumentBuilderDocumentBuilderFactory的更多信息。请注意,所提供的示例创建了一个XML DOM树,因此如果您有大量的XML数据,则该树可能很大。


更新:以下是一个示例,用于获取元素<requestqueue>的“value”。

protected String getString(String tagName, Element element) {
        NodeList list = element.getElementsByTagName(tagName);
        if (list != null && list.getLength() > 0) {
            NodeList subList = list.item(0).getChildNodes();

            if (subList != null && subList.getLength() > 0) {
                return subList.item(0).getNodeValue();
            }
        }

        return null;
    }

你可以有效地称之为:

String requestQueueName = getString("requestqueue", element);

5
为什么不使用内置的Java SE XPath库? - bdoughan
@Blaise Doughan,你可以这样做。我只是使用DOM来支持那些仍然活跃的遗留Java代码。 - Buhake Sindi
4
好的,如果有人仍然在使用JDK 1.4,那么你的方法是合理的。但如果他们使用的是1.5或者更新的版本,那么javax.xml.xpath库会更加方便。我不希望看到人们用更困难的方式去做一件事,而当有更好的方法存在时没有选择使用它。 - bdoughan
1
@Blaise Doughan,你会惊讶地发现有多少公司(尤其是银行)仍在运行JDK 1.4。 - Buhake Sindi
1
同意有一些开发者在使用JDK 1.4,并且你的解决方案适用于JDK 1.4。然而,如果他们正在使用JDK 1.5或更高版本,则javax.xml.xpath库更为适合。许多开发者以JDK 1.5和1.6作为基线。 - bdoughan

38

如果您只需要从XML中检索一个(第一个)值:

public static String getTagValue(String xml, String tagName){
    return xml.split("<"+tagName+">")[1].split("</"+tagName+">")[0];
}

如果您想解析整个XML文档,请使用JSoup:

Document doc = Jsoup.parse(xml, "", Parser.xmlParser());
for (Element e : doc.select("Request")) {
    System.out.println(e);
}

感谢指出jsoup - 在这里,纯Java真是个大麻烦。 - dermoritz
2
使用第一种方法,但出现了数组越界异常。 - MangduYogii
非常高效的解决方案。 - Jose Martinez
如果输入的 XML 包含 <![CDATA[ 块,则第一种解决方案会表现不正确。 - Klesun
@Klesun 说它会有点不正确的行为有点大胆。它将返回标签内的内容,而不管标签内有什么。你期望它做什么? - yurin
@yurin 使用第一种方法,但出现了数组越界异常。 - zordu-nickim-agzivi-sikm

29

如果你只想从XML中获取单个值,你可能需要使用Java的XPath库。有关示例,请参见我对先前问题的回答:

它将类似于:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class Demo {

    public static void main(String[] args) {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = domFactory.newDocumentBuilder();
            Document dDoc = builder.parse("E:/test.xml");

            XPath xPath = XPathFactory.newInstance().newXPath();
            Node node = (Node) xPath.evaluate("/Request/@name", dDoc, XPathConstants.NODE);
            System.out.println(node.getNodeValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

在节点 node 上找到了 NodeList,要求在 dDoc 中使用 xPath.evaluate("/Request/@name", dDoc, XPathConstants.NODE)。 - Piyush Mattoo
我们可以使用JAXB来实现相同的功能吗?它会比这种方法更好吗? - Prateek
你为什么要将 NodeList 强制转换为 Node,然后再进行赋值? - Joe M
@RajeevM - 这是个打字错误,现在已经修正了。 - bdoughan
1
@ziggy,XPath在JDK 1.4中从未存在过。 - Buhake Sindi
如果您正在尝试使用SOAP消息(或具有许多命名空间前缀的XML),那么很抱歉,这将是一件非常麻烦的事情。 - dermoritz

5

有许多不同的方法可以实现这一点。您可能想查看XStreamJAXB。有教程和示例。


@sam:你可能也想查看这个SO问题,https://dev59.com/QknSa4cB1Zd3GeqPQ8Us。 - Adeel Ansari

4
如果XML格式良好,您可以将其转换为文档。通过使用XPath,您可以获取XML元素。
String xml = "<stackusers><name>Yash</name><age>30</age></stackusers>";

从XML字符串创建文档并通过其XML路径查找元素。

Document doc = getDocument(xml, true);

    public static Document getDocument(String xmlData, boolean isXMLData) throws Exception {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setNamespaceAware(true);
        dbFactory.setIgnoringComments(true);
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc;
        if (isXMLData) {
            InputSource ips = new org.xml.sax.InputSource(new StringReader(xmlData));
            doc = dBuilder.parse(ips);
        } else {
            doc = dBuilder.parse( new File(xmlData) );
        }
        return doc;
    }

使用 org.apache.xpath.XPathAPI 来获取节点或节点列表。
System.out.println("XPathAPI:"+getNodeValue(doc, "/stackusers/age/text()"));

NodeList nodeList = getNodeList(doc, "/stackusers");
System.out.println("XPathAPI NodeList:"+ getXmlContentAsString(nodeList));
System.out.println("XPathAPI NodeList:"+ getXmlContentAsString(nodeList.item(0)));

    public static String getNodeValue(Document doc, String xpathExpression) throws Exception {
        Node node = org.apache.xpath.XPathAPI.selectSingleNode(doc, xpathExpression);
        String nodeValue = node.getNodeValue();
        return nodeValue;
    }
    public static NodeList getNodeList(Document doc, String xpathExpression) throws Exception {
        NodeList result = org.apache.xpath.XPathAPI.selectNodeList(doc, xpathExpression);
        return result;
    }

使用javax.xml.xpath.XPathFactory
System.out.println("javax.xml.xpath.XPathFactory:"+getXPathFactoryValue(doc, "/stackusers/age"));

    static XPath xpath = javax.xml.xpath.XPathFactory.newInstance().newXPath();
    public static String getXPathFactoryValue(Document doc, String xpathExpression) throws XPathExpressionException, TransformerException, IOException {
        Node node = (Node) xpath.evaluate(xpathExpression, doc, XPathConstants.NODE);
        String nodeStr = getXmlContentAsString(node);
        return nodeStr;
    }

使用文档元素。
System.out.println("DocumentElementText:"+getDocumentElementText(doc, "age"));

    public static String getDocumentElementText(Document doc, String elementName) {
        return doc.getElementsByTagName(elementName).item(0).getTextContent();
    }

在两个字符串之间获取值。
String nodeVlaue = org.apache.commons.lang.StringUtils.substringBetween(xml, "<age>", "</age>");
System.out.println("StringUtils.substringBetween():"+nodeVlaue);

完整示例:

public static void main(String[] args) throws Exception {
    String xml = "<stackusers><name>Yash</name><age>30</age></stackusers>";
    Document doc = getDocument(xml, true);
    
    String nodeVlaue = org.apache.commons.lang.StringUtils.substringBetween(xml, "<age>", "</age>");
    System.out.println("StringUtils.substringBetween():"+nodeVlaue);
    
    System.out.println("DocumentElementText:"+getDocumentElementText(doc, "age"));
    System.out.println("javax.xml.xpath.XPathFactory:"+getXPathFactoryValue(doc, "/stackusers/age"));
    
    System.out.println("XPathAPI:"+getNodeValue(doc, "/stackusers/age/text()"));
    NodeList nodeList = getNodeList(doc, "/stackusers");
    System.out.println("XPathAPI NodeList:"+ getXmlContentAsString(nodeList));
    System.out.println("XPathAPI NodeList:"+ getXmlContentAsString(nodeList.item(0)));
}
public static String getXmlContentAsString(Node node) throws TransformerException, IOException {
    StringBuilder stringBuilder = new StringBuilder();
    NodeList childNodes = node.getChildNodes();
    int length = childNodes.getLength();
    for (int i = 0; i < length; i++) {
        stringBuilder.append( toString(childNodes.item(i), true) );
    }
    return stringBuilder.toString();
}

输出:

StringUtils.substringBetween():30
DocumentElementText:30
javax.xml.xpath.XPathFactory:30
XPathAPI:30
XPathAPI NodeList:<stackusers>
   <name>Yash</name>
   <age>30</age>
</stackusers>
XPathAPI NodeList:<name>Yash</name><age>30</age>

1
你可以创建一个继承自org.xml.sax.helpers.DefaultHandler的类,并调用。
start_<tag_name>(Attributes attrs);

并且

end_<tag_name>();

对于它而言:

start_request_queue(attrs);

然后扩展该类并实现所需的xml配置文件解析器。例如:

  ...
  public void startElement(String uri, String name, String qname,
                           org.xml.sax.Attributes attrs) 
                  throws org.xml.sax.SAXException {
    Class[] args = new Class[2];
    args[0] = uri.getClass();
    args[1] = org.xml.sax.Attributes.class;
    try {
      String mname = name.replace("-", "");
      java.lang.reflect.Method m = 
            getClass().getDeclaredMethod("start" + mname, args);
      m.invoke(this, new Object[] { uri, (org.xml.sax.Attributes)attrs }); 
    }
catch (IllegalAccessException e) { throw new RuntimeException(e); }
catch (NoSuchMethodException e) { throw new RuntimeException(e); }
catch (java.lang.reflect.InvocationTargetException e) { org.xml.sax.SAXException se = new org.xml.sax.SAXException(e.getTargetException()); se.setStackTrace(e.getTargetException().getStackTrace()); }

在特定的配置解析器中:

public void start_Request(String uri, org.xml.sax.Attributes attrs) {
    // 确保正确读取属性
    System.err.println("请求,名称=" + attrs.getValue(0));
}



1

有两种常规方法可以实现这样的操作。您可以创建该 XML 文件的域对象模型,看一下 this

第二个选择是使用事件驱动解析,这是 DOM xml 表示的替代方法。在我看来,您可以在此处找到这两种基本技术的最佳综合比较。当然,关于处理 xml,还有更多需要了解的知识,例如如果您提供了 XML 模式定义 (XSD),您可以使用 JAXB


1

有许多API可用于通过Java读写XML文件。我建议使用StaX

此外,这可能会很有用 - Java XML APIs


1

由于您正在使用此配置,最好使用apache commons-configuration。对于简单的文件,它比“原始”XML解析器更容易使用。

请参阅XML how-to


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