2009-11-06 5 views
5

Я ищу простой Java сниппета, чтобы удалить пустые теги из (любой) структуры XMLJava Удалить пустые теги XML

<xml> 
    <field1>bla</field1> 
    <field2></field2> 
    <field3/> 
    <structure1> 
     <field4>bla</field4> 
     <field5></field5> 
    </structure1> 
</xml> 

должен превратиться;

<xml> 
    <field1>bla</field1> 
    <structure1> 
     <field4>bla</field4> 
    </structure1> 
</xml> 
+1

Вы в настоящее время разбора XML в структуры данных в каждом конкретном пути (JDOM, и т.д.)? Или вы начинаете с нуля? –

+3

Typo в примере XML: structure1 неправильно закрыт – Jonik

ответ

8

Мне было интересно, было бы легко сделать это с помощью библиотеки XOM и попробовать.

Это оказалось довольно легко:

import nu.xom.*; 

import java.io.File; 
import java.io.IOException; 

public class RemoveEmptyTags { 

    public static void main(String[] args) throws IOException, ParsingException { 
     Document document = new Builder().build(new File("original.xml")); 
     handleNode(document.getRootElement()); 
     System.out.println(document.toXML()); // empty elements now removed 
    } 

    private static void handleNode(Node node) { 
     if (node.getChildCount() == 0 && "".equals(node.getValue())) { 
      node.getParent().removeChild(node); 
      return; 
     } 
     // recurse the children 
     for (int i = 0; i < node.getChildCount(); i++) { 
      handleNode(node.getChild(i)); 
     } 
    } 
} 

Это, вероятно, не будет обрабатывать все случаи угловых должным образом, как совершенно пустой документ. И что делать с элементами, которые иначе пусты, но имеют атрибуты?

Если вы хотите сохранить XML-теги с атрибутами, мы можем добавить в методе «handleNode» следующая проверка:

... && ((Element) node).getAttributeCount() == 0)) 

Кроме того, если XML имеет два или более пустых тегов, один за другим; этот рекурсивный метод не удаляет все пустые теги!

(Этот ответ является частью моей оценки XOM как потенциального replacement to dom4j.)

+0

Спасибо, я буду использовать это – Raymond

0

С XSLT вы можете преобразовать свой XML, чтобы игнорировать пустые теги и переписывать документ.

8

Это таблицы стилей XSLT должен делать то, что вы ищете:

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

Он должен также сохранить элементы, которые являются пустыми, но имеют атрибуты, которые не являются. Если вы не хотите этого, то изменить:

<xsl:if test=". != '' or ./@* != ''">

To: <xsl:if test=". != ''">

Если вы хотите знать, как применять XSLT в Java, там должно быть много учебников там на межсетях , Удачи!

+0

+1 для решения XSLT –

3

В качестве примечания: Различные состояния тега на самом деле имеют смысл:

  • Open-Closed Tag : элемент существует и его значение является пустой строкой
  • Одно-тег: элемент существует, но значение null или nil
  • -
  • Отсутствует Метка: Элемент не существует

Таким образом, путем удаления пустых ОТКРЫТЫХ Закрытых тегов и Single-тегов, вы их слияния с группой недостающих тегов и таким образом потерять информацию.

+0

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

+0

Для моей цели это не имеет значения – Raymond

1

Если xml подан как строка; regex можно использовать для отфильтровывания пустых элементов:

<(\\w+)></\\1>|<\\w+/> 

Это найдет пустые элементы.

data.replaceAll(re, "") 

данные в этом случае переменная, содержащая вашу строку xml.
Не говорю, что это будет одним из лучших решений, но можно ...

1

мне нужно добавить стрип-пространство и отступы элементов в ответ Крис АиРа, иначе окружающие блоки, вновь пустой, не удаляется:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:output indent="yes" /> 
    <xsl:template match="@*|node()"> 
    <xsl:if test=". != '' or ./@* != ''"> 
     <xsl:copy> 
     <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 
0

Чтобы удалить все пустые теги, даже если они один за другим, один possibile решение:

private void removeEmptyTags(Document document) { 
    List<Node> listNode = new ArrayList<Node>(); 
    findListEmptyTags(document.getRootElement(), listNode); 
    if (listNode.size() == 0) 
     return; 

    for (Node node : listNode) { 
     node.getParent().removeChild(node); 
    } 
    removeEmptyTags(document); 
} 

private void findListEmptyTags(Node node, List<Node> listNode) { 

    if (node != null && node.getChildCount() == 0 && "".equals(node.getValue()) && ((Element) node).getAttributeCount() == 0) { 
     listNode.add(node); 
     return; 
    } 
    // recurse the children 
    for (int i = 0; i < node.getChildCount(); i++) { 
     findListEmptyTags(node.getChild(i), listNode); 
    } 
} 
2

Я проверил коды образца Jonik в и Марко. Но это не совсем то, что я хочу. Поэтому я изменил их источник, и ниже код работает хорошо для меня. Я уже настроил этот код в своем проекте. пожалуйста проверите его, если вы хотите.

public String removeEmptyNode(String xml){ 
    String cleanedXml = null; 
    try{ 
     xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + xml; 
     InputStream input = new ByteArrayInputStream(xml.getBytes("UTF-8")); 
     Document document = new Builder().build(input); 
     removeEmptyNode(document.getRootElement()); 
     cleanedXml = document.toXML(); 
    }catch(Exception e){ 
     e.printStackTrace(); 
    } 
    return cleanedXml; 
} 

private static void removeEmptyNode(Node node) { 
    if(node.getChildCount()!=0){ 
     int count = node.getChildCount(); 
     for (int i = count-1; i >= 0 ; i--) { 
      removeEmptyNode(node.getChild(i)); 
     } 
    } 

    doCheck(node); 
} 

private static void doCheck(Node node){ 
    if(node.getChildCount() == 0 && "".equals(node.getValue().trim())) { 
     try{node.getParent().removeChild(node);}catch(Exception e){} 
    }  
} 
0
public static void main(String[] args) { 

    final String regex1 = "<([a-zA-Z0-9-\\_]*)[^>]*/>"; 
    final String regex2 = "<([a-zA-Z0-9-\\_]*)[^>]*>\\s*</\\1>"; 

    String xmlString = "<xml><field1>bla</field1><field2></field2><field3/><structure1><field4><field50><field50/></field50></field4><field5></field5></structure1></xml>"; 
    System.out.println(xmlString); 

    final Pattern pattern1 = Pattern.compile(regex1); 
    final Pattern pattern2 = Pattern.compile(regex2); 

    Matcher matcher1; 
    Matcher matcher2; 
    do { 
     xmlString = xmlString.replaceAll(regex1, "").replaceAll(regex2, ""); 
     matcher1 = pattern1.matcher(xmlString); 
     matcher2 = pattern2.matcher(xmlString); 
    } while (matcher1.find() || matcher2.find()); 

    System.out.println(xmlString); 
} 

консоли:

<xml> 
    <field1>bla</field1> 
    <field2></field2> 
    <field3/> 
    <structure1> 
     <field4> 
      <field50> 
       <field60/> 
      </field50> 
     </field4> 
     <field5></field5> 
    </structure1> 
</xml> 

<xml> 
    <field1>bla</field1> 
</xml> 

Online demo here