代码之家  ›  专栏  ›  技术社区  ›  er4z0r

从XML中删除空属性

  •  3
  • er4z0r  · 技术社区  · 14 年前

    我有一个包含空属性的错误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。 但是,当我无法访问元素时,如何从元素中删除空属性?

    6 回复  |  直到 8 年前
        1
  •  1
  •   Eamon Nerbonne    8 年前

    下面的样式表将复制源文档中的所有内容,只包含空白的属性除外。第一个模板只是复制 一切 -包括空属性。但是,由于第二个模板使用了谓词,因此它的优先级高于第一个模板,因此当遇到空属性时,将优先选择更通用的第一个模板:而第二个模板不会生成任何输出。

    <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>
    
        2
  •  3
  •   Mads Hansen    14 年前

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

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

    要查找空属性或只有空白属性,请执行以下操作:

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

    这样就可以选择要删除的属性,而不必循环 每一个 属性只是为了找到空的。

        3
  •  1
  •   Don Roby    14 年前

    无论如何,这可能不是解决问题的方法。从节点列表中删除某些内容不会将其从XML中删除。如果您的解析器实际上正在处理一个已经加载的DOM,并且您正在在解析器得到它之前操作该DOM,那么类似的方法可能会奏效,但这可能不是最佳策略。

    通过在传递到解析器的过程中通过一个xmlfilter,您可能更好地对它进行预处理。我找到了一个 IBM Developerworks article 它是一个系列的一部分, earlier 演示如何将一系列过滤器连接到解析器。

    所有这些都假定您使用的是SAX解析器,但是如果它是其他东西,那么在某种类型的预处理步骤中,有可能使用SAX和类似的过滤器。

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

        4
  •  0
  •   Stefan De Boey    14 年前

    getParentNode()不适用于属性。

    所有节点(attr、document、documentfragment、entity和notation除外)都可以有父节点。

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

    //*[@*]
    

    然后您可以轻松地循环这些属性,并检查它们是否为空。

        5
  •  0
  •   M. Jessup    14 年前

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

    此外,如果使用查询选择元素节点而不是attr节点,则表达式n.gettexcontent()将查看文本内容,而不是属性值(如果根节点在列表中,则很可能是导致NPE的原因,因为大多数根节点没有文本内容),此外,还尝试删除attribu这将是一个不允许的行动(无论如何你也不打算这么做)。

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

        6
  •  0
  •   er4z0r    14 年前

    我真的找到了一种方法。尽管这并不能很好地解决这个问题,但目前还可以。如果使用此工具,请注意 只捕获值正好为“”的属性。其他的胡说八道(如只包含空白的值)将不会被捕获。

       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());
          }
         }
        }
    
       } 
    

    可惜的是,用于比较的regex只能作为一个xslt扩展而不能被授予在每个xslt处理器上受支持的权限:-(