2014-01-07 186 views
3

当我使用以下代码来修改的XML文件我收到此错误:为什么XPath.evaluate返回NULL?

Exception in thread "main" java.lang.NullPointerException 
    at ModifyXMLFile.updateFile(ModifyXMLFile.java:44) 
    at ModifyXMLFile.main(ModifyXMLFile.java:56) 

错误发生在线路:node.setTextContent(NEWVALUE);

我不正确使用xpath吗?

下面是代码,我试图更新

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.IOException; 
import java.io.InputStream; 

import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.parsers.ParserConfigurationException; 
import javax.xml.transform.Transformer; 
import javax.xml.transform.TransformerException; 
import javax.xml.transform.TransformerFactory; 
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamResult; 
import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants; 
import javax.xml.xpath.XPathExpression; 
import javax.xml.xpath.XPathExpressionException; 
import javax.xml.xpath.XPathFactory; 

import org.apache.commons.io.FileUtils; 
import org.w3c.dom.Document; 
import org.w3c.dom.Element; 
import org.w3c.dom.NamedNodeMap; 
import org.w3c.dom.Node; 
import org.w3c.dom.NodeList; 
import org.xml.sax.InputSource; 
import org.xml.sax.SAXException; 

public class ModifyXMLFile { 

    public void updateFile(String newValue){ 

     XPathFactory xPathfactory = XPathFactory.newInstance(); 
     XPath xpath = xPathfactory.newXPath(); 

     try { 

      File f = new File("C:\\pom.xml"); 
      InputStream in = new FileInputStream(f); 
      InputSource source = new InputSource(in); 

      Node node = (Node)xpath.evaluate("/project/parent/version/text()", source, XPathConstants.NODE); 
      node.setTextContent(newValue); 

     } catch (XPathExpressionException e) { 
      e.printStackTrace(); 
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     }  

    } 

    public static void main(String argv[]) { 

     new ModifyXMLFile().updateFile("TEST"); 

    } 

} 

xml文件的XML文件:

<?xml version="1.0" encoding="UTF-8"?> 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 

    <modelVersion>4.0.0</modelVersion> 

    <parent> 
     <groupId>testgroup</groupId> 
     <artifactId>testartifact</artifactId> 
     <version>update</version> 
    </parent> 

</project> 

回答

4
xmlns="http://maven.apache.org/POM/4.0.0" 

意味着XML文件中未加前缀的元素名称在这个命名空间中。在XPath 1.0中,前缀节点名称始终指代no名称空间中的节点,因此/project/parent/version正确匹配任何内容。

要匹配XPath中的名称空间节点,您需要将前缀绑定到名称空间URI,然后在表达式中使用该前缀。对于javax.xml.xpath这意味着创建一个NamespaceContext。不幸的是在标准的Java库这个界面没有默认实现,但是Spring框架提供了一个SimpleNamespaceContext,您可以使用

XPath xpath = xPathfactory.newXPath(); 
SimpleNamespaceContext nsCtx = new SimpleNamespaceContext(); 
xpath.setNamespaceContext(nsCtx); 
nsCtx.bindNamespaceUri("pom", "http://maven.apache.org/POM/4.0.0"); 

// ... 
Node node = (Node)xpath.evaluate("/pom:project/pom:parent/pom:version/text()", source, XPathConstants.NODE); 

这就是说,你仍然需要做一些更多的工作,以实际修改该文件,因为您目前正在加载它并修改DOM,但不会将修改的DOM保存到任何地方。

另一种方法可能是使用XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
       xmlns:pom="http://maven.apache.org/POM/4.0.0"> 
    <xsl:param name="newVersion" /> 

    <!-- identity template - copy input to output unchanged except when 
     overridden --> 
    <xsl:template match="@*|node()"> 
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy> 
    </xsl:template> 

    <!-- override for the version value --> 
    <xsl:template match="pom:version/text()"> 
    <xsl:value-of select="$newVersion" /> 
    </xsl:template> 
</xsl:stylesheet> 

然后你可以使用Transformer API来调用这个样式表用适当的参数

StreamSource xslt = new StreamSource(new File("transform.xsl")); 
Transformer transformer = TransformerFactory.newInstance().newTransformer(xslt); 
transformer.setParameter("newVersion", newValue); 
StreamSource input = new StreamSource(new File("C:\\pom.xml")); 
StreamResult output = new StreamResult(new File("C:\\updated-pom.xml")); 
transformer.transform(input, output); 
3

由于XML文档的名称空间限定(参见project元素中的xmlns属性):

<project xmlns="http://maven.apache.org/POM/4.0.0" ... 

您需要在XPath对象上设置javax.xml.namespace.NamespaceContext的实现。这用于返回XPath中单个步骤的名称空间信息。

// SET A NAMESPACECONTEXT 
    xpath.setNamespaceContext(new NamespaceContext() { 

     @Override 
     public Iterator getPrefixes(String namespaceURI) { 
      return null; 
     } 

     @Override 
     public String getPrefix(String namespaceURI) { 
      return null; 
     } 

     @Override 
     public String getNamespaceURI(String prefix) { 
      if("pom".equals(prefix)) { 
       return "http://maven.apache.org/POM/4.0.0"; 
      } 
      return null; 
     } 
    }); 

你需要改变你的XPath以包括NamespaceContext使用的前缀。现在您只是不在寻找名为project的元素,您正在寻找名称为project的名称空间限定元素,NamespaceContext将解析前缀以匹配您正在查找的实际URI。

Node node = (Node)xpath.evaluate("/pom:project/pom:parent/pom:version/text()", source, XPathConstants.NODE); 
-3

@Adrain请使用以下XPath,你应该能够读取或更改值 /父母/版本/文()

+1

你看过其他的答案?由于默认的名称空间声明,这将不起作用。 –