【问题标题】:Remove Empty Attributes from XML从 XML 中删除空属性
【发布时间】:2010-03-19 11:15:04
【问题描述】:

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

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

   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。 但是,当我无法访问元素时,如何从元素中删除空属性?

【问题讨论】:

  • 该标签应该是 xslt 而不是 xlst?
  • 哎呀!喜欢多眼球原则;-)
  • 如果您在问题中根本没有提到 XSLT,为什么还要用“xslt”来标记它?
  • 呃。又对了。没有像“OneOfThoseManyXMLStandardsIkeepMixingUp”这样的标签;-)

标签: java xml xpath jaxp


【解决方案1】:

如果你想把它限制为空属性,你可以使用这个 XPATH:

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

要查找为空或只有空格的属性:

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

这样您就可以选择要删除的属性,而不必为了找到空的属性而遍历每个属性。

【讨论】:

  • 我认为您在第二个表达式中有错字。至少我的 XSLT-Processor 抱怨 `expecting ] found 。
  • @er4z0r - 哎呀!当我为第二个表达式添加normalize-space() 时,我已经离开了.。我已经更正了答案。仅供参考 - 如果我把它放在 normalize-space() 函数中(即 normalize-space(.) ),我可以把它留在里面。无论哪种方式都有效。
  • 谢谢。通过检查 xpath 函数文档找到它;-)
【解决方案2】:

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

<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>

【讨论】:

  • 我刚刚开始掌握 XSLT。你能告诉我为什么你的身份转换(这就是你通常所说的复制一切,对吗?)没有 select="@*|node()|text()"
  • 我可以通过引用标准 (w3.org/TR/xslt#patterns) 来回答这个问题:“node() 匹配除属性节点和根节点之外的任何节点”。我们不关心根节点/,但关心属性——因此是@*
  • 阅读规范揭示了有趣的事实:原来规范有一个示例复制模板,它与上面的字面上相同:w3.org/TR/xslt#copying
  • 感谢您的帮助。我知道我应该阅读规范,但它有点冗长。
  • 别打扰 - 我使用 zvon,它在 99% 的时间里都更易读 - 当你真的需要详细信息时,它会链接到规范的相应部分. (然而,CSS 规范实际上比许多网络指南更易于理解和精确得多,这实际上值得经常查看)。
【解决方案3】:

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

您最好在它到达解析器的途中通过一个 XMLFilter 对其进行预处理。我找到了一个IBM Developerworks article,其中包含删除所有属性的示例代码,它是earlier 展示如何将过滤器链连接到解析器的系列的一部分。

所有这些都假设您使用的是 SAX 解析器,但如果是其他东西,则可能有一些方法可以在某种预处理步骤中使用 SAX 和这样的过滤器。

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

【讨论】:

    【解决方案4】:

    getParentNode() 不适用于属性。

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

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

    //*[@*]
    

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

    【讨论】:

    • 谢谢。就在一分钟前解决这个问题;-)。
    【解决方案5】:

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

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

    所以如果你接收的是元素节点而不是属性节点,那么你应该查看属性映射然后修改它,如果你必须查看所有属性,你最好只写一个深度优先- 搜索查看 DOM 并在那里执行修改。

    【讨论】:

      【解决方案6】:

      我实际上找到了一种方法。尽管这不能完美地解决问题,但没关系。目前。在使用它的情况下,请注意,它只会捕获值恰好是“其他废话”的属性,例如仅由空格组成的值不会被它捕获。

         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-Processor 上受支持:-(

      【讨论】:

      • 但是,EXSL 得到广泛支持,包括正则表达式支持。如果另一种选择是将自己限制在 java 实现中,那么无论如何你都不会失去任何可移植性......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-18
      • 2023-03-08
      • 2022-09-23
      • 1970-01-01
      相关资源
      最近更新 更多