Как получить желаемый вывод XML из двух XML с использованием XSLT - PullRequest
0 голосов
/ 03 ноября 2010

Я использую XSLT и XML.

Первым делом я собираюсь работать над двумя XML.

Первый XML:

<?xml version="1.0"?>
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ID="tcm:232-83752-2" Managed="10682">  
  <tcm:Item ID="tcm:232-564598" Title="010 News Mapping"/>
  <tcm:Item ID="tcm:232-564599" Title="020 CUGOs"/>
  <tcm:Item ID="tcm:232-614307" Title="030 Reserved Urls"/>
</tcm:ListItems>

Второй XML , который мы получим, используя вышеуказанный идентификатор, т.е. tcm: 232-564598 и т. Д., Ниже приведен один из XML для идентификатора tcm: 232-564598, а другие идентификаторы будут иметь тот же тип XML .

<tcm:Component ID="tcm:229-564598" IsEditable="false" xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink">
  <tcm:Data>
    <tcm:Content>
      <MappingCollection xmlns="uuid:922EEC29-2DE3-4BA1-A46A-A300CB8FA85F">
        <VanityUrl>
          <old>mbp</old>
          <new>/SessionHandler.aspx?pageurl=/BP.aspx&amp;pub=/english&amp;section=IBE&amp;j=f</new>
          <dateAdded>2010-05-03T14:45:00</dateAdded>
          <comments> News mapping </comments>
        </VanityUrl>
        <VanityUrl>
          <old>about/news</old>
          <new>about/news/news.aspx</new>
          <dateAdded>2010-05-03T14:45:00</dateAdded>
          <comments> News mapping </comments>
        </VanityUrl>
      </MappingCollection>
    </tcm:Content>
  </tcm:Data>
</tcm:Component>

Я пытаюсь получить формат XML ниже, используя оба XML.

<?xml version="1.0" encoding="UTF-8"?>
<mappings>
    <!-- News mapping -->
    <mapping old="mbp" new="/SessionHandler.aspx?pageurl=/BP.aspx&amp;pub=/english&amp;section=IBE&amp;j=f"/>
    <mapping old="about/news" new="about/news/news.aspx"/>
    <!-- CUGO's-->
    <mapping old="/nhs" new="/cugo.aspx?promoCode=UKNHS01&amp;pub=/uk/english"/>
    <mapping old="/hk/ukstudentfare" new="/cugo.aspx?promoCode=HKSTU10&amp;pub=/hk/Chinese"/>
</mappings> 

А вот мой XSLT, где я пытаюсь сгенерировать вышеуказанный формат XML, но он не работает для меня. обратите внимание, что сначала xml - это первичный xml, который будет преобразован с использованием ниже XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:em="http://www.espire.com/tridion/schemas" xmlns:tcmse="http://www.tridion.com/ContentManager/5.1/TcmScriptAssistant" exclude-result-prefixes="em xlink tcmse tcm">
  <xsl:output method="xml" version="1.0" encoding="UTF-16" indent="yes"/>
  <!-- root match-->
  <xsl:template match="tcm:ListItems">
    <mappings>
      <xsl:apply-templates select="tcm:Item"/>
    </mappings>
  </xsl:template>
  <xsl:template match="tcm:Item">
      <xsl:variable name="doc" select="document(@ID)"/>
    <xsl:if test="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments">
      <xsl:comment>
        <xsl:value-of select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments"/>
      </xsl:comment>
    </xsl:if>  
    <xsl:for-each select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl">
        <xsl:element name="mapping">
          <xsl:if test="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:old">
            <xsl:attribute name="old">
              <xsl:value-of select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:old"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:if test="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:new">
            <xsl:attribute name="new">
              <xsl:value-of select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:new"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:if test="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:dateAdded">
            <xsl:attribute name="dateAdded">
              <xsl:value-of select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:dateAdded"/>
            </xsl:attribute>
          </xsl:if>
        </xsl:element>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

В приведенном выше xslt я могу получить, что цикл данных также идет правильно, но поступающие данные такие же, я имею в виду, что цикл работает правильно, но значение узла такое же

Пожалуйста, предложите!

Ответы [ 3 ]

2 голосов
/ 03 ноября 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ns="uuid:922EEC29-2DE3-4BA1-A46A-A300CB8FA85F"
 xmlns:tcm="http://www.tridion.com/ContentManager/5.0"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="ns tcm msxsl">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kVanityByComment" match="ns:VanityUrl" use="ns:comments"/>
    <xsl:template match="/">
        <xsl:variable name="vSourcesRTF">
            <xsl:copy-of select="document(tcm:ListItems/tcm:Item/@ID)"/>
        </xsl:variable>
        <mappings>
            <xsl:apply-templates select="msxsl:node-set($vSourcesRTF)/node()"/>
        </mappings>
    </xsl:template>
    <xsl:template match="ns:VanityUrl"/>
    <xsl:template match="ns:VanityUrl[generate-id()=
                                      generate-id(key('kVanityByComment',
                                                      ns:comments)[1])]">
        <xsl:comment>
            <xsl:value-of select="ns:comments"/>
        </xsl:comment>
        <xsl:apply-templates select="key('kVanityByComment',
                                         ns:comments)"
                             mode="output"/>
    </xsl:template>
    <xsl:template match="ns:VanityUrl" mode="output">
        <mapping>
            <xsl:apply-templates/>
        </mapping>
    </xsl:template>
    <xsl:template match="ns:VanityUrl/ns:comments" priority="1"/>
    <xsl:template match="ns:VanityUrl/*">
        <xsl:attribute name="{local-name()}">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

С этим входом:

<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ID="tcm:232-83752-2" Managed="10682">
  <tcm:Item ID="229-564598" Title="010 News Mapping"/>
  <tcm:Item ID="229-564598" Title="020 CUGOs"/>
  <tcm:Item ID="229-564598" Title="030 Reserved Urls"/>
</tcm:ListItems>

И этот внешний источник с 229-564598 URI:

<tcm:Component ID="tcm:229-564598" IsEditable="false" xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink">
  <tcm:Data>
    <tcm:Content>
      <MappingCollection xmlns="uuid:922EEC29-2DE3-4BA1-A46A-A300CB8FA85F">
        <VanityUrl>
          <old>mbp</old>
          <new>/SessionHandler.aspx?pageurl=/BP.aspx&amp;pub=/english&amp;section=IBE&amp;j=f</new>
          <dateAdded>2010-05-03T14:45:00</dateAdded>
          <comments> News mapping </comments>
        </VanityUrl>
        <VanityUrl>
          <old>about/news</old>
          <new>about/news/news.aspx</new>
          <dateAdded>2010-05-03T14:45:00</dateAdded>
          <comments> News mapping </comments>
        </VanityUrl>
      </MappingCollection>
    </tcm:Content>
  </tcm:Data>
</tcm:Component>

Выход:

<mappings>
    <!-- News mapping -->
    <mapping old="mbp" 
             new="/SessionHandler.aspx?pageurl=/BP.aspx&amp;pub=/english&amp;section=IBE&amp;j=f"
             dateAdded="2010-05-03T14:45:00"></mapping>
    <mapping old="about/news"
             new="about/news/news.aspx"
             dateAdded="2010-05-03T14:45:00"></mapping>
</mappings>

РЕДАКТИРОВАТЬ : несколько источников входного сигнала.

1 голос
/ 16 января 2013

Вы можете попробовать это тоже !!

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:em="http://www.espire.com/tridion/schemas" xmlns:tcmse="http://www.tridion.com/ContentManager/5.1/TcmScriptAssistant" exclude-result-prefixes="em xlink tcmse tcm">
  <xsl:output method="xml" version="1.0" encoding="UTF-16" indent="yes"/>     
  <!-- root match-->
  <xsl:template match="tcm:ListItems">
    <mappings>
      <xsl:apply-templates select="tcm:Item"/>
    </mappings>
  </xsl:template>
  <xsl:template match="tcm:Item">
    <xsl:variable name="doc" select="document(@ID)"/>
    <xsl:if test="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments">
      <xsl:comment>
        <xsl:value-of select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments"/>
      </xsl:comment>
    </xsl:if>
    <xsl:for-each select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl">
      <xsl:element name="mapping">
        <xsl:if test="em:old">
          <xsl:attribute name="old">
            <xsl:value-of select="em:old"/>
          </xsl:attribute>
        </xsl:if>
        <xsl:if test="em:new">
          <xsl:attribute name="new">
            <xsl:value-of select="em:new"/>
          </xsl:attribute>
        </xsl:if>
        <xsl:if test="em:dateAdded">
          <xsl:attribute name="dateAdded">
            <xsl:value-of select="em:dateAdded"/>
          </xsl:attribute>
        </xsl:if>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
1 голос
/ 03 ноября 2010

Да, внесенные вами изменения действительно важны. : -)

Что делает цикл for-each, это выбирает каждый элемент <em:VanityUrl>, соответствующий вашему выражению XPath, делает этот элемент контекстным узлом для того, что находится внутри for-each (называемый template , даже если это не <xsl:template>), а затем создать экземпляр этого внутреннего шаблона с новым узлом контекста.

Когда вы продолжали использовать «$ doc / ...» внутри цикла for-each, вы отбрасывали узел контекста, поэтому for-each не имел никакого эффекта (за исключением повторения n раз).

Ваши операторы <xsl:if test="$doc/..."> оценивали наличие такого узла во всем документе вместо элемента контекста <em:VanityUrl>.

Оператор <xsl:value-of> обращает внимание только на первый узел в выбранном наборе узлов, поэтому вы всегда получали значение из first <em:VanityUrl>, независимо от узла контекста.

Когда вы начали выбирать и тестировать относительно узла контекста:

    <xsl:if test="em:old">

все стало лучше. : -)

Вы попросили внести ценный вклад. По стилистическим соображениям вы можете заменить свои <xsl:if> тесты на <xsl:apply-templates>. (Во-первых, @Dimitre Novatchev порадует вас. :-) Так что

<xsl:if test="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments">
  <xsl:comment>
    <xsl:value-of select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments"/>
  </xsl:comment>
</xsl:if>

становится

<xsl:apply-templates select="$doc/tcm:Component/tcm:Data/tcm:Content/em:MappingCollection/em:VanityUrl/em:comments" />

и тогда вам нужен отдельный шаблон для них:

<xsl:template match="em:comments">
  <xsl:comment>
    <xsl:value-of select="."/>
  </xsl:comment>
</xsl:template>

Есть несколько преимуществ для этого. Самое главное, что вам не нужно дублировать это длинное выражение XPath, которое было подвержено ошибкам, если выражение когда-либо нужно было изменить. Если нет элементов em: comment, apply-templates ничего не будут делать, поэтому не будут выдавать комментарий.

Он также модулирует вашу таблицу стилей, так что вы можете изменить способ отображения em: комментариев отдельно от места их возникновения. Это может не иметь большого значения в простых XML-документах, где em: comments встречается только в одном месте, но именно этот стиль наилучшим образом использует возможности XSLT. Также обратите внимание, что эта измененная версия будет выводить несколько комментариев, если есть несколько em: комментариев, которых нет в вашей версии. Опять же, у вас, вероятно, нет множителей на входе, поэтому это не имеет значения.

Аналогично для выходных атрибутов:

    <xsl:if test="em:old">
      <xsl:attribute name="old">
        <xsl:value-of select="em:old"/>
      </xsl:attribute>
    </xsl:if>

может стать

    <xsl:apply-templates select="em:old[1]" />

с отдельным шаблоном

<xsl:template match="em:old">
   <xsl:attribute name="old">
     <xsl:value-of select="."/>
   </xsl:attribute>
</xsl:template>

Обратите внимание на [1], в котором избегается попытка вывести несколько атрибутов old="..." для одного и того же элемента, если входные данные em: VanityUrl имеют несколько элементов em: old. Это может привести к ошибке в вашей таблице стилей. Но, может быть, вы хотите вызвать ошибку в этом случае. Если это так, вы, вероятно, уже проверяете свой входной XML.

На самом деле вы можете обобщить apply-шаблоны и шаблон здесь, чтобы применить их ко всем трем атрибутам:

    <xsl:apply-templates select="em:old[1] | em:new[1] | em:dateAdded[1]" />

Опять же, если какой-либо из этих элементов отсутствует, для них ничего не будет сделано (пустой атрибут не будет создан). Шаблон:

<xsl:template match="em:old | em:new | em:dateAdded">
   <xsl:attribute name="{local-name()}">
     <xsl:value-of select="."/>
   </xsl:attribute>
</xsl:template>

local-name() дает нам имя элемента без префикса пространства имен.

Обновление:

Еще один способ справиться с этим - использовать режим:

    <xsl:apply-templates select="em:old[1] | em:new[1] | em:dateAdded[1]"
      mode="make-attribute" />

<xsl:template match="em:*" mode="make-attribute">
   <xsl:attribute name="{local-name()}">
     <xsl:value-of select="."/>
   </xsl:attribute>
</xsl:template>

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

Единственное, что я хотел бы сказать, это то, что использование указанных выше пространств имен сбивает с толку ... оно не должно работать как есть. Например. Ваша таблица стилей использует этот URI пространства имен для таких элементов, как VanityURL:

 "http://www.espire.com/tridion/schemas"

но второй входной документ использует этот URI пространства имен для этих элементов:

 "uuid:922EEC29-2DE3-4BA1-A46A-A300CB8FA85F"

Неважно, что префиксы пространства имен отличаются ("em:" и default), но URI пространства имен должны совпадать. Я предполагаю, что URI пространства имен VanityURL должен был измениться, иначе ваша таблица стилей не будет работать ...

НТН!

...