Как узнать узел элемента с пустым строковым значением в xsl - PullRequest
2 голосов
/ 13 февраля 2009

Я работаю над преобразованием XML-файла из старой версии в новую. Вот основной шаблон, который я использую:

<xsl:template match="*">
    <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
      <xsl:copy-of select="@*"></xsl:copy-of>
      <xsl:apply-templates></xsl:apply-templates>
    </xsl:element>
</xsl:template>

Однако новая версия XML-схемы требует, чтобы все элементы, имеющие текстовое значение, не были пустой строкой. Так старый XML-документ, такой как:

<dataset>
 <title> </title>
</dataset>

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

<xsl:template match="text()">
    <xsl:variable name="text-value" select="."/>
      <xsl:if test="normalize-space($text-value) = ''">
          <xsl:message terminate="yes">
                <xsl:call-template name="output_message3_fail">
                  <xsl:with-param name="parent_node" select="name(parent::node())"/>
                </xsl:call-template>
          </xsl:message>
      </xsl:if>
      <xsl:value-of select="$text-value"/>
</xsl:template>

Тем не менее, я узнал, выглядит ли ввод:

<dataset>
 <title>My tile</title>
</dataset

будет вызван новый текстовый шаблон. Если вход выглядит так:

<dataset>
 <title> </title>
</dataset>

новый текстовый шаблон никогда не будет вызываться, а вывод будет выглядеть как

<dataset>
     <title/>
</dataset>

Так что мой подход - изменение текстового шаблона, не работает. У вас есть какие-либо предложения, как это сделать - если найдете элемент с пустой строкой, прекратите преобразование.

Большое спасибо!

Кстати, я использую процессор Java XALAN XSLT.

Ответы [ 3 ]

2 голосов
/ 13 февраля 2009

Однако я выяснил, выглядит ли вход как:

<dataset>
  <title>My tile</title>
</dataset>

новый текстовый шаблон будет называться

Да, именно это и должен делать предоставленный код - я объясню это чуть позже.

Если вход выглядит так:

<dataset>
  <title> </title>
</dataset>

новый текстовый шаблон никогда не будет вызывается и вывод будет выглядеть как

<dataset>
  <title/>
</dataset>

Я не мог воспроизвести это с Xalan (J или c) и многими другими процессорами XSLT У меня есть (Saxon 6.5.3, .NET XslCompiledTransform и XslTransform, Msxml3,4, 6, JD ,. .. так далее). Все они отображают сообщение об ошибке (внутри <xsl:message terminate="yes">)

Единственный процессор XSLT, который выдает вышеуказанный вывод, - это AltovaXML (XmlSPY).

Если вы используете XmlSPY, возможно, вы могли бы попробовать либо использовать другой процессор XSLT, либо обратиться за помощью к Altova.

Теперь вернемся к первому поведению.

Объяснение

Предоставленный исходный XML-файл:

<dataset>
  <title>My tile</title>
</dataset>

имеет три текстовых узла :

  1. Первый текстовый узел находится между <dataset> и <title> и содержит только пробел.

  2. Второй текстовый узел является единственным потомком <title>, а его значением является строка «Моя плитка».

  3. Третий и последний текстовый узел находится между </title> и </dataset> и состоит только из пробелов.

Когда шаблон, соответствующий text(), выбран для обработки первого из трех вышеупомянутых текстовых узлов, тест считается положительным и выполняется <xsl:message terminate="yes"> - и это именно то поведение, о котором сообщается.

Решение

Существует простое решение. Просто измените шаблон, соответствующий text(), чтобы он соответствовал только тем текстовым узлам, которые являются единственным текстовым узлом их родителя. Теперь преобразование XSLT ведет себя как и ожидалось для обоих типов документов XML, которые были изначально предоставлены :

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

  <xsl:template match="*">
    <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

  <xsl:template match=
    "*[not(node()[2])]/text()
              [normalize-space()='']">
    <xsl:message terminate="yes">
      <xsl:call-template name="output_message3_fail">
        <xsl:with-param name="parent_node" select="name(..)"/>
      </xsl:call-template>
    </xsl:message>
  </xsl:template>

  <xsl:template name="output_message3_fail">
    <xsl:param name="parent_node"/>

    <xsl:message>        ERROR:        
      &lt;<xsl:copy-of select="$parent_node"/>> is empty
    </xsl:message>
  </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному документу XML:

<dataset>
  <title>My tile</title>
</dataset>

Требуемый результат получен :

<dataset>
   <title>My tile</title>
</dataset>

Когда он применяется ко второму документу XML :

<dataset>
    <title> </title>
</dataset>

получен правильный результат:

ERROR:        
        <title> is empty
0 голосов
/ 15 февраля 2009

Может быть, тест должен быть что-то вроде

length(text())!=0 && length(strip-whitespace(text())) == 0

Разве xslt не поддерживает регулярные выражения? Если это так, то это будет путь.

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

<foo bar="BAR"/>

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

Что подводит меня к моему последнему комментарию: правильная технология проверки достоверности документа XML - это схема XML.

0 голосов
/ 13 февраля 2009

Мне не ясно, чего именно вы действительно хотите. Вы говорите, что не хотите создавать элементы, содержащие пустую строку, а затем приводите в качестве примера:

<dataset>
   <title> </title>
</dataset>

, в котором элемент title не содержит пустую строку. Он содержит пробелы. Поэтому я предполагаю, что под «пустой строкой» вы подразумеваете «только пробел».

Использование xsl:strip-space удалит текстовые узлы, содержащие только пробелы, из исходного дерева перед его обработкой. Если вы действительно хотите прервать преобразование с исключением, если вы столкнулись с элементом, содержащим пробел, вы не можете использовать xsl:strip-space, так как он удалит все условия запуска исключения до запуска преобразования.

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

<xsl:template match="*[not(*) and text() and not(normalize-space(text()) != '')]">
   ...

Этот шаблон будет соответствовать любому элементу, для которого верно следующее:

  • не имеет дочерних элементов
  • содержит хотя бы один текстовый узел
  • все содержащиеся в нем текстовые узлы только для пробелов

Так что в вашем примере он не будет соответствовать элементу dataset (потому что у него есть дочерний элемент), но он будет соответствовать элементу title. Однако он не будет соответствовать <title/> или <title></title>, поскольку ни один из этих элементов не содержит текстовых узлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...