XSLT - Как отобразить элементы на пары имя-значение?

Мне нужно перевести XML из исходного формата в пары целевых значений имени для общей обработки. Любые советы о том, как этого добиться, пожалуйста? Я пытаюсь использовать MapForce, если это проще.

От

<products>
    <product>
        <type>Monitor</type>
        <size>22</size>
        <brand>EIZO</brand>
    </product>
    <product>
        ......
    </product>
</products>

в

<products>
    <product num="1">
        <attribute name="type">Monitor</attribute>
        <attribute name="size">22</attribute>
        <attribute name="brand">EIZO</attribute>
    </product>
    <product num="2">
        ....
    </product>
</products>

Я предполагаю, что мне нужно использовать xsl: for-each в элементе для генерации элемента?

Как насчет атрибута "num", это просто счетчик в принципе. это может быть позиция ()?

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

5 ответов

Для подобных проблем вы часто начинаете с создания шаблона идентификации XSLT

<xsl:template match="@*|node()">
   <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
   </xsl:copy>
</xsl:template>

Само по себе это копирует все узлы как есть, что означает, что вам нужно только написать соответствующие шаблоны для узлов, которые вы хотите преобразовать.

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

<xsl:template match="product">
   <product num="{position()}">
      <xsl:apply-templates select="@*|node()"/>
   </product>
</xsl:template>

Обратите внимание на использование здесь шаблонов значений атрибутов при создании атрибута num. Фигурные скобки указывают на выражение, которое нужно оценить, а не выводить буквально.

Затем вы хотите, чтобы шаблон соответствовал дочерним элементам элементов продукта, и превращаете их в узлы атрибутов. Это сделано с шаблоном, чтобы соответствовать любому такому ребенку, как так

<xsl:template match="product/*">
   <attribute name="{local-name()}">
      <xsl:apply-templates />
   </attribute>
</xsl:template>

Обратите внимание, что <xsl:apply-templates /> можно заменить на <xsl:value-of select="." /> здесь, если вы только когда-либо будете иметь текстовые узлы в дочерних элементах.

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="product">
      <product num="{position()}">
         <xsl:apply-templates select="@*|node()"/>
      </product>
   </xsl:template>

   <xsl:template match="product/*">
      <attribute name="{local-name()}">
         <xsl:apply-templates />
      </attribute>
   </xsl:template>
</xsl:stylesheet>

Применительно к вашему XML выводится следующее

<products>
  <product num="1">
    <attribute name="type">Monitor</attribute>
    <attribute name="size">22</attribute>
    <attribute name="brand">EIZO</attribute>
  </product>
  <product num="2">
        ......
  </product>
</products>

Конечно, если вы действительно хотите превратить дочерние элементы в надлежащие атрибуты, в отличие от элементов с именем "attribute", вы должны использовать команду xsl:attribute. Заменить последний шаблон этим

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

При использовании этого шаблона вместо этого выводится следующее (ну, это будет включать продукт 2, если в вашем образце есть дочерние элементы для него!)

<products>
  <product num="1" type="Monitor" size="22" brand="EIZO"></product>
  <product num="2">
    ......
  </product>
</products>

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

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

<xsl:template match="products">
    <products>
        <xsl:call-template name="transformProducts">
            <xsl:with-param name="nodes" select="product"/>
        </xsl:call-template>
    </products>
</xsl:template>

<xsl:template name="transformProducts">
    <xsl:param name="nodes"/>
    <xsl:param name="counter" select="0"/>

    <xsl:choose>
        <xsl:when test="not($nodes)"></xsl:when>
        <xsl:otherwise>
            <product>
                <xsl:attribute name="num">
                    <xsl:value-of select="$counter + 1"/>
                </xsl:attribute>
                <xsl:attribute name="type">
                    <xsl:value-of select="$nodes[1]/type"/>
                </xsl:attribute>
                <xsl:attribute name="size">
                    <xsl:value-of select="$nodes[1]/size"/>
                </xsl:attribute>
                <xsl:attribute name="brand">
                    <xsl:value-of select="$nodes[1]/brand"/>
                </xsl:attribute>
            </product>
            <xsl:call-template name="transformProducts">
                <xsl:with-param name="nodes" select="$nodes[position() > 1]"/>
                <xsl:with-param name="counter" select="$counter + 1"/>
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Согласно моему тесту, вот начальный XML:

<products>    
 <product>
    <type>Monitor</type>
    <size>22</size>
    <brand>EIZO</brand>
</product>
<product>
    <type>MonitorTwo</type>
    <size>32</size>
    <brand>SONY</brand>
</product>
<product>
    <type>what</type>
    <size>12</size>
    <brand>VGA</brand>
</product>
</products>

И вот вывод. Я предполагаю, что, называя ваши атрибуты "атрибутом", вы на самом деле имели в виду их преобразование в узлы атрибутов элемента продукта?

<products>
 <product num="1" type="Monitor" size="22" brand="EIZO"/>
 <product num="2" type="MonitorTwo" size="32" brand="SONY"/>
 <product num="3" type="what" size="12" brand="VGA"/>
</products>

Конечно, небеса запрещают, чтобы это было коротким и простым, потому что... почему на самом деле?

Версия A (элементы с именем "attribute"):

<xsl:template match="/">
<products>
    <xsl:for-each select="products/product">
        <product num="{position()}">
           <xsl:for-each select="*">
             <attribute name="{name()}">
                <xsl:value-of select="."/>
             </attribute>
           </xsl:for-each>
        </product>
    </xsl:for-each>
</products>
</xsl:template>

Версия B (реальные атрибуты):

<xsl:template match="/">
<products>
    <xsl:for-each select="products/product">
        <product num="{position()}">
           <xsl:for-each select="*">
             <xsl:attribute name="{name()}">
                <xsl:value-of select="."/>
             </xsl:attribute>
           </xsl:for-each>
        </product>
    </xsl:for-each>
</products>
</xsl:template>

Спасибо всем,

Подходы Тима и Филлипса очень разные, но оба работают отлично. Правильно ли я думаю

  • Подход Тима основан на исходном XML, а затем использует шаблоны для настройки того, что указано. Скажем, если у меня есть 100 элементов и я хочу настроить только несколько из них, то это хороший вариант.

  • Подход Филиппа - это создание нового XML с нуля, но извлечение / извлечение XPath того, что я хочу. Скажем, если у меня есть 100 элементов, и я хочу извлечь и преобразовать только несколько элементов для вывода, то это хороший вариант.

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

Не проверял это, но вы могли бы сделать что-то вроде следующего:

<products>
    <xsl:for-each select="//product">
        <product num="{position()}">
           <xsl:for-each select="current()/*">
             <attribute name="{name()}">
                <xsl:value-of select="self::node()/text()"/>
             </attribute>
           </xsl>
        </product>
    </xsl>
</products>
Другие вопросы по тегам