Удаление пустых тегов из XML через XSLT

У меня был xml следующего шаблона

<?xml version="1.0" encoding="UTF-8"?>
    <Person>
      <FirstName>Ahmed</FirstName>
      <MiddleName/>
      <LastName>Aboulnaga</LastName>
      <CompanyInfo>
        <CompanyName>IPN Web</CompanyName>
        <Title/>
    <Role></Role>
        <Department>
    </Department>
      </CompanyInfo>
    </Person>

Я использовал следующий xslt (полученный из форумов) в попытке удалить пустые теги

 <xsl:template match="@*|node()">
<xsl:if test=". != '' or ./@* != ''">
  <xsl:copy>
  <xsl:copy-of select = "@*"/>
    <xsl:apply-templates />
  </xsl:copy>
</xsl:if>

Используемый xslt успешно удаляет теги, такие как

<Title/>
    <Role></Role>

... но не работает, когда пустые теги находятся на двух строках, например:

<Department>
    </Department>

Есть ли какое-либо исправление для этого?

Ответ 1

Это преобразование вообще не требует каких-либо условных инструкций XSLT и не использует явных приоритетов:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
    "*[not(@*|*|comment()|processing-instruction()) 
     and normalize-space()=''
      ]"/>
</xsl:stylesheet>

При применении к предоставленному XML-документу:

<Person>
    <FirstName>Ahmed</FirstName>
    <MiddleName/>
    <LastName>Aboulnaga</LastName>
    <CompanyInfo>
        <CompanyName>IPN Web</CompanyName>
        <Title/>
        <Role></Role>
        <Department>
        </Department>
    </CompanyInfo>
</Person>

он производит желаемый, правильный результат:

<Person>
   <FirstName>Ahmed</FirstName>
   <LastName>Aboulnaga</LastName>
   <CompanyInfo>
      <CompanyName>IPN Web</CompanyName>
   </CompanyInfo>
</Person>

Ответ 2

<xsl:template match="@*|node()">
  <xsl:if test="normalize-space(.) != '' or ./@* != ''">
    <xsl:copy>
       <xsl:copy-of select = "@*"/>
       <xsl:apply-templates/>
    </xsl:copy>
  </xsl:if>
</xsl:template>

Ответ 3

(..) Есть ли какое-либо исправление для этого?

Тег на двух строках не является пустым тегом. Это тег, содержащий пробелы внутри (например, новые строки и, возможно, некоторые символы пробела). Функция XPath 1.0 normalize-space() позволяет нормализовать содержимое ваших тегов, удалив ненужные новые строки.

После того, как вы применили функцию к содержимому тега, вы можете проверить пустую строку. Хороший способ сделать это - применить функцию XPath 1.0 boolean() к содержимому тега. Если содержимое представляет собой строку нулевой длины, ее результат будет false.

Наконец, вы можете вставлять все, что немного меняет ваше преобразование личности. Вам не нужны инструкции xsl:if или любой другой дополнительный шаблон.

Окончательное преобразование будет выглядеть так:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
             <xsl:apply-templates 
                  select="node()[boolean(normalize-space())]
                         |@*"/>
     </xsl:copy>
 </xsl:template>

</xsl:stylesheet>

Дополнительная заметка

В настоящее время ваша команда xsl:if также проверяет наличие пустых атрибутов. Таким образом вы фактически удаляете также теги, не содержащие empy, с пустыми атрибутами. Это не похоже на "Удаление пустых тегов". Поэтому будьте осторожны, или вы сомневаетесь в том, что вам не хватает деталей, или вы используете небезопасный код.

Ответ 4

Ваш вопрос не указан. Что означает пустое? Здесь <outer> пуст?

<outer><inner/></outer>

В любом случае, здесь один подход, который может соответствовать вашему счету:

<xsl:template match="*[not(.//@*) and not( normalize-space() )]" priority="3"/>

Обратите внимание, что вам может потребоваться настроить приоритет в соответствии с вашими потребностями.

Ответ 5

Из того, что я нашел в сети, это самый правильный ответ:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:template match="/">
        <xsl:apply-templates select="*"/>
    </xsl:template>
    <xsl:template match="*">
            <xsl:if test=".!=''">
                <xsl:copy>
                  <xsl:copy-of select="@*"/>
                  <xsl:apply-templates/>
                </xsl:copy>
            </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Ответ 6

Вы можете использовать следующий xslt для удаления пустых тегов/атрибутов:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="node()">
        <xsl:if test="normalize-space(string(.)) != ''
                        or count(@*[normalize-space(string(.)) != '']) > 0
                        or count(descendant::*[normalize-space(string(.)) != '']) > 0
                        or count(descendant::*/@*[normalize-space(string(.)) != '']) > 0">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
        </xsl:if>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:if test="normalize-space(string(.)) != ''">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
            </xsl:copy>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>