XSLT: преобразование структуры элементов на основе значений атрибутов - PullRequest
1 голос
/ 20 января 2010

Может кто-нибудь помочь мне решить следующую проблему, вот входной XML, XSLT, который я использую, и ожидаемый вывод. На самом деле я знаю, что из-за того, что не сгенерирован уникальный generateid, этот xslt не может сгенерировать желаемый вывод, но я не знаю, куда этот код должен быть вставлен.

XML:

<item id="N65537" text="catalog">
  <item id="N65540" text="cd">
    <item id="N65542" text="title">
      <item id="N65543" img="VAL" text="Empire Burlesque" />
    </item>
    <item id="N65545" text="artist">
      <item id="N65546" img="VAL" text="Bob Dylan" />
    </item>
    <item id="N65548" text="country">
      <item id="N65549" text="attr1" img="ATTR">
        <item id="N65549_N65549" text="primary" img="ATTRVAL" />
      </item>
      <item id="N65550" img="VAL" text="USA" />
    </item>
    <item id="N65552" text="company">
      <item id="N65553" text="attr2" img="ATTR">
        <item id="N65553_N65553" text="main" img="ATTRVAL" />
      </item>
      <item id="N65554" img="VAL" text="Columbia" />
    </item>
    <item id="N65556" text="price">
      <item id="N65557" img="VAL" text="10.90" />
    </item>
    <item id="N65559" text="year">
      <item id="N65560" img="VAL" text="1985" />
    </item>
  </item>
  <item id="N65563" text="cd">
    <item id="N65565" text="title">
      <item id="N65566" img="VAL" text="Hide your heart" />
    </item>
    <item id="N65568" text="artist">
      <item id="N65569" img="VAL" text="Bonnie Tyler" />
    </item>
    <item id="N65571" text="country">
      <item id="N65572" img="VAL" text="UK" />
    </item>
    <item id="N65574" text="company">
      <item id="N65575" img="VAL" text="CBS Records" />
    </item>
    <item id="N65577" text="price">
      <item id="N65578" img="VAL" text="9.90" />
    </item>
    <item id="N65580" text="year">
      <item id="N65581" img="VAL" text="1988" />
    </item>
  </item>
</item>

XSLT:

 <xsl:stylesheet version="1.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="/">
   <xsl:call-template name="dispatch">
     <xsl:with-param name="nodes" select="node()"/>
   </xsl:call-template>
 </xsl:template>

 <xsl:template name="dispatch">
   <xsl:param name="nodes"/>
   <xsl:choose>
     <xsl:when test="text()">
       <xsl:call-template name="apply" >
         <xsl:with-param name="select" select="node()" />
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise>
       <xsl:call-template name="apply" />
     </xsl:otherwise>
   </xsl:choose>
 </xsl:template>  

 <xsl:template name="apply">
   <xsl:param name="select" select="node()" />
   <xsl:for-each select="$select">
     <xsl:if test='local-name() !=""'>
       <xsl:variable name="ename"> 
         <xsl:for-each select="@*">
           <xsl:if test='name()="img1"'>
             <xsl:text><xsl:value-of select="." /></xsl:text> 
           </xsl:if>
         </xsl:for-each> 
       </xsl:variable> 
       <xsl:variable name="aname"> 
         <xsl:for-each select="@*">
           <xsl:if test='name()="img"'>
             <xsl:text><xsl:value-of select="." /></xsl:text> 
           </xsl:if>
         </xsl:for-each> 
       </xsl:variable> 

       <xsl:for-each select="@*">
         <xsl:variable name="tname"> 
           <xsl:text><xsl:value-of select="." /></xsl:text> 
         </xsl:variable> 
         <xsl:choose>
           <xsl:when test='name() ="text" and normalize-space($ename) = "VAL" and normalize-space($aname) != "ATTR"'>
             <xsl:element name="{$tname}"> 
               <xsl:for-each select="$select">
                 <xsl:call-template name="dispatch"/>
               </xsl:for-each>
             </xsl:element> 
           </xsl:when>
           <xsl:when test='name() ="text" and normalize-space($ename) = "VAL" '>
             <xsl:value-of select="$tname" />
           </xsl:when>
           <xsl:when test='name() ="text" and normalize-space($aname) = "ATTR"'>
             <xsl:attribute name="id"><xsl:value-of select="$aname" /></xsl:attribute>
           </xsl:when>
         </xsl:choose>
       </xsl:for-each>
     </xsl:if>
   </xsl:for-each>
 </xsl:template>
 </xsl:stylesheet>

Ожидаемый результат:

<catalog> 
  <cd>
    <title>Empire Burlesque</title> 
    <artist>Bob Dylan</artist> 
    <country attr1="primary">USA</country> 
    <company attr2="main">Columbia</company> 
    <price>10.90</price> 
    <year>1985</year> 
  </cd> 
  <cd> 
    <title>Hide your heart</title> 
    <artist>Bonnie Tyler</artist> 
    <country>UK</country> 
    <company>CBS Records</company> 
    <price>9.90</price> 
    <year>1988</year> 
  </cd> 
</catalog> 

Ответы [ 4 ]

3 голосов
/ 20 января 2010

РЕДАКТИРОВАТЬ: модифицированный ответ после детализации был добавлен к вопросу.

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <!-- normal items become an ordinary element -->
  <xsl:template match="item">
    <xsl:element name="{@text}">
      <!-- attributes must be created before any other contents -->
      <xsl:apply-templates select="item[@img='ATTR']" />
      <!-- now process sub-elements and values (i.e. "anything else") -->
      <xsl:apply-templates select="item[not(@img='ATTR')]" />
    </xsl:element>
  </xsl:template>

  <!-- items with "ATTR" become an attribute -->
  <xsl:template match="item[@img='ATTR']">
    <xsl:attribute name="{@text}">
      <xsl:value-of select="item[@img='ATTRVAL']/@text" />
    </xsl:attribute>
  </xsl:template>

  <!-- items with "VAL" become a simple text -->
  <xsl:template match="item[@img='VAL']">
    <xsl:value-of select="@text" />
  </xsl:template>

</xsl:stylesheet>

дает

<catalog>
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country attr1="primary">USA</country>
    <company attr2="main">Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your heart</title>
    <artist>Bonnie Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
</catalog>

Таблица стилей работает, потому что процессор XSL выбирает шаблоны на основе специфики их соответствиявыражения.match="item[@img='ATTR']" более конкретно, чем match="item", поэтому для каждого обработанного <item> (через <xsl:apply-templates select="item" />) механизм автоматически выбирает нужный шаблон.

1 голос
/ 20 января 2010

Основная проблема, которую я вижу в вашем решении XSLT, состоит в том, что вы используете xsl: if и xsl: select вместо 'select' для фильтрации узлов.Это делает ваш XSLT трудным для чтения и понимания (по крайней мере, для меня).

Попробуйте это:

<?xml version="1.0" encoding="iso-8859-1"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes"/>

   <xsl:template match="/item[@text='catalog']">
     <catalog>
       <xsl:apply-templates select="item[@text='cd']"></xsl:apply-templates>
     </catalog>
   </xsl:template>

   <xsl:template match="item[@text='cd']">
     <cd>
       <title><xsl:value-of select="item[@text='title']/item[@img1='VAL']/@text"/></title>
       <artist><xsl:value-of select="item[@text='artist']/item[@img1='VAL']/@text"/></artist>
       <country><xsl:value-of select="item[@text='country']/item[@img1='VAL']/@text"/></country>
       <company><xsl:value-of select="item[@text='company']/item[@img1='VAL']/@text"/></company>
       <price><xsl:value-of select="item[@text='price']/item[@img1='VAL']/@text"/></price>
       <year><xsl:value-of select="item[@text='year']/item[@img1='VAL']/@text"/></year>
     </cd>
   </xsl:template>
 </xsl:stylesheet>

Решение не распространяется на узлы ATTR, поскольку они не являются частью описанного результата.

0 голосов
/ 20 января 2010

Как насчет этого:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.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="/">
        <catalog>
            <xsl:for-each select="/item[@text='catalog']/item[@text='cd']">
               <cd>
                   <xsl:for-each select="item">
                       <xsl:variable name="ename" select="string(@text)"/>
                       <xsl:variable name="value" select="item/@text"/>
                       <xsl:element name="{$ename}">
                           <xsl:value-of select="$value"/>
                       </xsl:element>
                   </xsl:for-each>
               </cd>                 
            </xsl:for-each>
        </catalog>
    </xsl:template>    
</xsl:stylesheet>

Не так хорошо, как решение Томалакс - но возможно немного яснее относительно намерения.

0 голосов
/ 20 января 2010

Если вы можете изменить входной XML, сделайте это. Предполагается, что XML несет определенный смысл в именах тегов и в своей структуре. Вызов все item просто делает его нечитаемым.

Внесение такого изменения также позволит вам написать читаемый XSLT, который не прибегает к приемам выбора иерархии узлов.

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