Удалить пустые атрибуты из XML - PullRequest
3 голосов
/ 19 марта 2010

У меня глючный 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());
     }
    }

   } 

Этот код дает мне NPE при доступе к n.getParentNode (). GetAttributes (). Но как я могу удалить пустой атрибут из элемента, когда я не могу получить доступ к элементу?

Ответы [ 6 ]

3 голосов
/ 20 марта 2010

Если вы хотите ограничить его только пустыми атрибутами, вы можете использовать этот XPATH:

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

Чтобы найти атрибуты, которые либо пусты, либо имеют только пробелы:

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

Таким образом, вы выбираете атрибуты, которые хотите удалить, и вам не нужно зацикливаться на каждом отдельном атрибуте только для поиска пустых.

1 голос
/ 21 марта 2010

Следующая таблица стилей скопирует все содержимое исходного документа, кроме атрибутов, которые содержат только пробелы. Первый шаблон просто копирует все - включая пустые атрибуты. Однако второй шаблон имеет более высокий приоритет, чем первый из-за использования предиката, поэтому он будет выбран предпочтительнее более общего первого шаблона при обнаружении пустого атрибута: и этот второй шаблон не создает выход.

<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>
1 голос
/ 19 марта 2010

Это, вероятно, не способ сделать это в любом случае. Удаление чего-либо из вашего NodeList не приведет к его удалению из XML. Если ваш синтаксический анализатор фактически обрабатывает уже загруженный DOM, и вы манипулируете DOM до того, как синтаксический анализатор получит его, это может сработать, но это, вероятно, не лучшая тактика.

Вероятно, вам лучше предварительно обработать его, передав его через XMLFilter на пути к парсеру. Я нашел статью IBM Developerworks с примером кода, который удаляет все атрибуты, и он входит в серию, которая ранее показывает, как подключить цепочку фильтров к вашему анализатору.

Все это предполагает, что вы используете синтаксический анализатор SAX, но если это что-то другое, вероятно, есть способы использования SAX и такого фильтра на каком-то этапе предварительной обработки.

Также возможно, что вы можете выполнить предварительную обработку с помощью xslt.

0 голосов
/ 19 марта 2010

Я действительно нашел способ сделать это. Несмотря на то, что это не решит проблему совершенно, это О.К. теперь. В случае использования этого, имейте в виду, что он будет перехватывать только те атрибуты, которые имеют значение, которое является точно '' другой ерундой, например, значение, состоящее только из пробела, не будет поймано этим.

   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-процессоре: - (

0 голосов
/ 19 марта 2010

Я бы проверил, чтобы убедиться, что вы на самом деле получаете списки только узлов типа ATTR, а не элементов или их комбинации. Я не использовал XPathExpression, однако он может интерпретировать путь «// @ *» как «любой элемент с атрибутом», а не «все атрибуты» (что, как я ожидаю, вы имеете в виду). Если первый равен true, а ваш корневой узел имеет атрибут, он будет отображаться в результирующем списке узлов из запроса и по определению [корневой узел] .getParentNode () == null, создающий ваш NPE.

Кроме того, если вы выбираете с помощью запроса узлы элементов, а не узлы attr, выражение n.getTextContent () будет искать текстовое содержимое, а не значение атрибута (опять же, вероятная причина, ведущая к вашему NPE, если корневой узел находится в списке, так как большинство корневых узлов не имеют текстового содержимого), кроме того, попытка удаления атрибута была бы запрещена (что вы в действительности не намереваетесь).

Так что, если вы получаете узлы элементов вместо узлов атрибутов, то вам следует взглянуть на карту атрибутов, а затем изменить ее, и если вам нужно просмотреть все атрибуты, вам может быть лучше просто написать Depth-First -Поиск, глядя на DOM и выполняя изменения там.

0 голосов
/ 19 марта 2010

getParentNode () не работает с атрибутами.

Все узлы, кроме Attr, Document, DocumentFragment, Entity и Notation, могут иметь родителя.

не уверен на 100%, но я думаю, что вы можете выбрать все узлы, которые имеют атрибут со следующим выражением:

//*[@*]

затем вы можете легко перебрать атрибуты и проверить, не являются ли они пустыми

...