Вопрос преобразования XSL

Следующий вопрос: у меня есть XML-документ "InputDoc" и файл xslt для преобразования в другой XML-документ "OutputDoc". Вы можете найти примеры документов xslt и xml ниже.

<?xml version="1.0" encoding="UTF-8"?>
<InputDoc>
  <InputCollection>
    <InputItem>
      <InputValue>Value_1</InputValue>
    </InputItem>
  </InputCollection>
</InputDoc>

<?xml version="1.0" encoding="utf-16"?>
<OutputDoc>
  <OutputElement>
    <OutputItem>
      <OutputValue>Value_1</OutputValue>
    </OutputItem>
    <OutputDescription>Description_1</OutputDescription>
  </OutputElement>
</OutputDoc>

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="//InputCollection">
    <OutputDoc>
      <xsl:for-each select="InputItem">
        <OutputElement>
          <OutputItem>
            <OutputValue><xsl:value-of select="InputValue" /></OutputValue>
          </OutputItem>
          <OutputDescription>
            <xsl:call-template name="InputValue2OutputDescriptionMappings">
              <xsl:with-param name="InputValueParam" select="InputValue" />
            </xsl:call-template>
          </OutputDescription>
        </OutputElement>
      </xsl:for-each>
    </OutputDoc>
  </xsl:template>

  <xsl:template name="InputValue2OutputDescriptionMappings">
    <xsl:param name="InputValueParam" />
        <xsl:choose>
            <xsl:when test="$InputValueParam='Value_1'">Description_1</xsl:when>
            <xsl:when test="$InputValueParam='Value_2'">Description_2</xsl:when>
            <xsl:otherwise></xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Оно работает. Но было бы хорошо, если бы выходной xml-документ содержал только узлы «OutputElement», которые содержат значения в «InputValue2OutputDescriptionMappings», т.е. если значение узла «OutputDescription» пусто, то узел «OutputElement» не будет включен в «OutputDoc».

Как я могу это сделать, используя приведенное выше преобразование XSL?


person Maxim Polishchuk    schedule 14.07.2010    source источник
comment
Этот пост очень похож на то, что вам нужно stackoverflow.com/questions/825831/   -  person Longball27    schedule 14.07.2010
comment
Ребята ответили, что мне нужно :) Я не хотел знать, как определять пустое значение, у меня была другая проблема.   -  person Maxim Polishchuk    schedule 15.07.2010
comment
Мы все еще догадываемся, что вы хотите, поскольку ваш пример не показывает интересующий вас случай. Обновите образцы ввода / вывода, чтобы показать то, что вы хотите.   -  person Jim Garrison    schedule 15.07.2010


Ответы (2)


Это стандартная таблица стилей XSLT 1.0, оптимизированная с помощью key():

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="description" match="description" use="@value"/>
    <xsl:variable name="map">
        <description value="Value_1">Description_1</description>
        <description value="Value_2">Description_2</description>
    </xsl:variable>
    <xsl:template match="InputDoc">
        <OutputDoc>
            <xsl:apply-templates/>
        </OutputDoc>
    </xsl:template>
    <xsl:template match="InputCollection">
        <xsl:variable name="me" select="."/>
        <xsl:for-each select="document('')">
            <xsl:if test="key('description',$me/InputItem/InputValue)">
                <OutputElement>
                    <xsl:apply-templates select="$me/*"/>
                    <xsl:apply-templates select="key('description',$me/InputItem/InputValue)"/>
                </OutputElement>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="InputItem">
        <OutputItem>
            <xsl:apply-templates/>
        </OutputItem>
    </xsl:template>
    <xsl:template match="InputValue">
        <OutputValue>
            <xsl:apply-templates/>
        </OutputValue>
    </xsl:template>
    <xsl:template match="description">
        <OutputDescription>
            <xsl:apply-templates/>
        </OutputDescription>
    </xsl:template>
</xsl:stylesheet>

Результат:

<OutputDoc>
    <OutputElement>
        <OutputItem>
            <OutputValue>Value_1</OutputValue>
        </OutputItem>
        <OutputDescription>Description_1</OutputDescription>
    </OutputElement>
</OutputDoc>

Примечание: «Соответствие шаблону». key() с несколькими документами (в данном случае таблица стилей).

Изменить: пропустите полосу OutputElement для запроса несоответствующего значения, извините.

Теперь этот ввод:

<InputDoc>
  <InputCollection>
    <InputItem>
      <InputValue>Value_3</InputValue>
    </InputItem>
  </InputCollection>
</InputDoc>

Выход:

<OutputDoc></OutputDoc>

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

person Community    schedule 14.07.2010
comment
‹? Xml version = 1.0 encoding = UTF-8?› ‹InputDoc› ‹InputCollection› ‹InputItem› ‹InputValue› Value_3 ‹/InputValue› ‹/InputItem› ‹/InputCollection› ‹/InputDoc› Выходной xml должен быть пустым после преобразования, потому что Значение Value_3 не существует в списке переменных, но выходной документ содержит узлы со значением Value_3. - person Maxim Polishchuk; 15.07.2010
comment
@Maxim Polishchuk: Ответ изменен. Извините. - person ; 15.07.2010

Сначала вы должны получить результат сопоставления, ЗАТЕМ проверить его на пустоту и опустить тег <OutputDescription>, как в:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="//InputCollection">
    <OutputDoc>
      <xsl:for-each select="InputItem">
        <OutputElement>
          <OutputItem>
            <OutputValue><xsl:value-of select="InputValue" /></OutputValue>
          </OutputItem>
          <xsl:variable name="desc">
            <xsl:call-template name="InputValue2OutputDescriptionMappings">
              <xsl:with-param name="InputValueParam" select="InputValue" />
            </xsl:call-template>
          </xsl:variable>
          <xsl:if test="string-length($desc/text())">
            <OutputDescription><xsl:value-of select="$desc"/></OutputDescription>
          </xsl:if>
        </OutputElement>
      </xsl:for-each>
    </OutputDoc>
  </xsl:template>

  <xsl:template name="InputValue2OutputDescriptionMappings">
    <xsl:param name="InputValueParam" />
    <xsl:choose>
      <xsl:when test="$InputValueParam='Value_1'">Description_1</xsl:when>
      <xsl:when test="$InputValueParam='Value_2'">Description_2</xsl:when>
      <xsl:otherwise></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Однако здесь есть гораздо более XSL-версия, которая хранит сопоставления в переменной, чтобы обходиться без шаблона сопоставления, и использует обработку «push» (apply-templates) вместо обработки «pull».

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="//InputCollection">
    <OutputDoc>
      <xsl:apply-templates select="InputItem"/>
    </OutputDoc>
  </xsl:template>

  <xsl:variable name="mapping">
    <map input="Value_1" output="Description_1"/>
    <map input="Value_2" output="Description_2"/>
  </xsl:variable>

  <xsl:template match="InputItem">
    <OutputElement>
      <OutputItem>
        <OutputValue><xsl:value-of select="InputValue" /></OutputValue>
      </OutputItem>
      <xsl:variable name="input" select="InputValue/text()"/>
      <xsl:variable name="desc" select="$mapping/map[@input=$input]/@output"/>
      <xsl:if test="$desc">
        <OutputDescription><xsl:value-of select="$desc"/></OutputDescription>
      </xsl:if>
    </OutputElement>
  </xsl:template>

</xsl:stylesheet>
person Jim Garrison    schedule 14.07.2010
comment
В XSLT 1.0 непустые переменные являются фрагментом дерева результатов, и вы не можете использовать с ними оператор /. Итак, вы запускаете таблицу стилей с процессором XLST 2.0 или с нестандартным процессором XSLT 1.0. - person ; 15.07.2010
comment
Тот же результат, что и в xslt ниже. ‹? Xml version = 1.0 encoding = UTF-8?› ‹InputDoc› ‹InputCollection› ‹InputItem› ‹InputValue› Value_3 ‹/InputValue› ‹/InputItem› ‹/InputCollection› ‹/InputDoc› Выходной xml должен быть пустым после преобразования, если значение (в примере Value_3) не существует в списке переменных, но выходной документ содержит узлы со значением Value_3. - person Maxim Polishchuk; 15.07.2010
comment
@Alejandro: XSL-процессор - это Saxon 6.5 под Oxygen / XML. Я не использовал EXSLT. - person Jim Garrison; 15.07.2010