从XML中删除空属性

时间:2010-03-19 11:15:05

标签: java xml xpath jaxp

我有一个包含空属性的错误xml,我有一个解析空属性的解析器。 我无法控制xml的生成,也无法控制在空的attrs上咳嗽的解析器。所以我想做的是一个预处理步骤,它只是删除所有空属性。

我设法找到空属性,但现在我不知道如何删除它们:

   XPathFactory xpf = XPathFactory.newInstance();
   XPath xpath = xpf.newXPath();
   XPathExpression expr = xpath.compile("//@*");
   Object result = expr.evaluate(d, XPathConstants.NODESET);

   if (result != null) {
    NodeList nodes = (NodeList) result;
    for(int node=0;node<nodes.getLength();node++)
    {
     Node n = nodes.item(node);
     if(isEmpty(n.getTextContent()))
     {
      this.log.warn("Found empty attribute declaration "+n.toString());
      NamedNodeMap parentAttrs = n.getParentNode().getAttributes();
      parentAttrs.removeNamedItem(n.getNodeName());
     }
    }

   } 

这段代码在访问n.getParentNode()。getAttributes()时给了我一个NPE。 但是当我无法访问元素时,如何从元素中删除空属性?

6 个答案:

答案 0 :(得分:3)

如果要将其限制为空属性,可以使用此XPATH:

//*[@*[.='']]

要查找空或仅包含空格的属性:

//*[@*[normalize-space()='']]

通过这种方式,您可以选择要删除的属性,而不必遍历每个属性,只是为了找到空的属性。

答案 1 :(得分:1)

无论如何,这可能不是这样做的方法。从NodeList中删除某些内容不会将其从XML中删除。如果您的解析器实际上正在处理已经加载的DOM,并且您在解析器获取之前操作DOM,那么类似于此可能会起作用,但这可能不是最好的策略。

通过在传递给解析器的过程中通过XMLFilter传递它,可能会更好地预处理它。我找到了IBM Developerworks article,其中包含删除所有属性的示例代码,它是earlier演示如何将一系列过滤器连接到解析器的系列文章的一部分。

所有这些都假设您正在使用SAX解析器,但如果它是其他东西,则有可能在某种预处理步骤中使用SAX和此类过滤器。

您也可以通过xslt进行预处理。

答案 2 :(得分:1)

以下样式表将复制源文档中的所有内容 - 除了仅包含空格的属性。第一个模板只是复制所有内容 - 包括空属性。但是,由于使用了谓词,第二个模板的优先级高于第一个模板,这就是为什么在遇到空属性时会优先选择更通用的第一个模板的原因:第二个模板不生成任何一个输出

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="@*[normalize-space()='']"/>
</xsl:stylesheet>

答案 3 :(得分:0)

getParentNode()不适用于属性。

  

除Attr,Document,DocumentFragment,Entity和Notation之外的所有节点都可能有父节点。

不是100%肯定,但我认为您可以选择具有以下表达式属性的所有节点:

//*[@*]

然后,您可以轻松地遍历属性并检查它们是否为空

答案 4 :(得分:0)

我会检查以确保您实际上只接收ATTR类型的节点列表,而不是Elements,或两者的混合。我没有使用过XPathExpression但是它可以将路径“// @ *”解释为“具有属性的任何元素”而不是“所有属性”(我希望你的意思)。如果前者是真的,并且您的根节点具有属性,它将出现在查询的结果节点列表中,并且根据定义[root node] .getParentNode()== null生成您的NPE。

此外,如果您选择元素节点而不是您的查询的attr节点,则表达式n.getTextContent()将查看文本内容,而不是属性值(如果根节点再次导致您的NPE的可能原因)在列表中,因为大多数根节点都没有文本内容),另外尝试删除属性将是一个无操作(无论如何你都不打算这样做)。

因此,如果您正在接收元素节点而不是属性节点,那么您应该查看属性映射然后对其进行修改,如果您必须查看所有属性,那么您可能最好只编写深度优先 - 搜索DOM并在那里进行修改。

答案 5 :(得分:0)

我实际上找到了一种方法。虽然这不能完全解决问题,但是O.K.目前。在使用它的情况下,请注意,它将只捕获具有正确值的其他废话的属性,例如仅由空格组成的值将不被此捕获。

   XPathFactory xpf = XPathFactory.newInstance();
   XPath xpath = xpf.newXPath();
   XPathExpression expr = xpath.compile("//*[@*='']");
   Object result = expr.evaluate(d, XPathConstants.NODESET);

   if (result != null) {
    NodeList nodes = (NodeList) result;
    for(int node=0;node<nodes.getLength();node++)
    {
     Node n = nodes.item(node);
     NamedNodeMap attrs = n.getAttributes();
     for(int attr=0;attr<attrs.getLength();attr++)
     {
      Node a = attrs.item(attr);
      if(isEmpty(a.getNodeValue()));
      {
       attrs.removeNamedItem(a.getNodeName());
       this.log.warn("Removing empty attribute "+a.toString()+" from element "+n.getNodeName());
      }
     }
    }

   } 

用于比较的可敬正则表达式仅作为XSLT扩展提供,并且不被授予在每个XSLT处理器上支持: - (