Удаление пустых тегов из 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>
Есть ли какое-то решение для этого?
6 ответов
Это преобразование вообще не нуждается в каких-либо условных инструкциях 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>
<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>
(..) Есть ли какое-то решение для этого?
Тег на двух строках не является пустым тегом. Это тег, содержащий пробелы внутри (например, новые строки и, возможно, какие-то символы пробела). Функция XPath 1.0 normalize-space()
позволяет нормализовать содержимое ваших тегов, удаляя ненужные новые строки.
После применения функции к содержимому тега вы можете проверить наличие пустой строки. Хороший способ сделать это - применить XPath 1.0. boolean()
функция для тега содержимого. Если содержимое является строкой нулевой длины, его результат будет ложным.
Наконец, вы можете встроить все, слегка изменяя свое преобразование личности. Вы не должны 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 с пустыми атрибутами. Это не звучит как "Удаление пустых тегов". Поэтому будьте осторожны, либо в вашем вопросе отсутствуют какие-либо детали, либо вы используете небезопасный код.
Вы можете использовать следующий 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>
Из того, что я нашел в сети, это самый правильный ответ:
<?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>
Ваш вопрос не указан. Что значит пустой? Является <outer>
здесь пусто?
<outer><inner/></outer>
Во всяком случае, вот один подход, который может соответствовать вашему счету:
<xsl:template match="*[not(.//@*) and not( normalize-space() )]" priority="3"/>
Обратите внимание, что вам, возможно, придется настроить приоритет в соответствии со своими потребностями.