Слияние и переименование полей в xslt - PullRequest
1 голос
/ 02 февраля 2011

Я новичок в xslt, и я боюсь, что эта проблема связана с тем, что я застрял в процедурном мышлении.

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

Ввод:

<variables> 
    <var value="a-value"> a </var>
    <var value="b-value"> b </var>
    <var value="c-value"> c </var>
    <var value="d-value"> d </var>
</variables>

Желаемый вывод:

<variables> 
    <var value="new-a-value"> new-a </var>
    <var value="new-b-and-c-value"> new-b-and-c </var>
    <var value="new-d-value"> new-d </var>
</variables>

Мой xslt:

  <xsl:for-each select="var">
    <xsl:choose>

      <xsl:when test="@value = 'a-value'">
        <var value="new-a-value"> new-a </var>
      </xsl:when>

      <xsl:when test="@value = 'b-value'">
        <var value="new-b-and-c-value"> new-b-and-c </var>
      </xsl:when>

      <xsl:when test="@value = 'c-value'">
        <var value="new-b-and-c-value"> new-b-and-c </var>
      </xsl:when>

      <xsl:when test="@value = 'd-value'">
        <var value="new-d-value"> new-d </var>
      </xsl:when>

    </xsl:choose>
  </xsl:for-each>

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

Ответы [ 3 ]

3 голосов
/ 03 февраля 2011

Рефакторинг : Делаем проще, чтобы реальная проблема стала яснее: группировка по значению сопоставленного ключа.

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

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:v="value"
 exclude-result-prefixes="v">
    <xsl:key name="kVarByReplace"
             match="var"
             use="document('')/*/v:v[@o=current()/@value]/@n"/>
    <v:v o="a-value" n="new-a-value"/>
    <v:v o="b-value" n="new-b-and-c-value"/>
    <v:v o="c-value" n="new-b-and-c-value"/>
    <v:v o="d-value" n="new-d-value"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="var">
        <xsl:variable name="vKey"
                      select="document('')/*/v:v[@o=current()/@value]/@n"/>
        <xsl:if test="count(.|key('kVarByReplace',$vKey)[1]) = 1">
            <var value="{$vKey}">
                <xsl:value-of select="$vKey"/>
            </var>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Выход:

<variables>
    <var value="new-a-value">new-a-value</var>
    <var value="new-b-and-c-value">new-b-and-c-value</var>
    <var value="new-d-value">new-d-value</var>
</variables>

Примечание : Использование функции document() в xsl:key/@use.

Это показывает, насколько легко это будет в XSLT 2.0:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="vMap" as="element()*">
        <v o="a-value">new-a-value</v>
        <v o="b-value">new-b-and-c-value</v>
        <v o="c-value">new-b-and-c-value</v>
        <v o="d-value">new-d-value</v>
    </xsl:variable>
    <xsl:template match="variables">
        <variables>
            <xsl:for-each-group select="var"
                                group-by="$vMap[@o=current()/@value]">
                <var value="{current-grouping-key()}">
                    <xsl:value-of select="current-grouping-key()"/>
                </var>
            </xsl:for-each-group>
        </variables>
    </xsl:template>
</xsl:stylesheet>

Первое решение с ключом также для карты.

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:v="value"
 exclude-result-prefixes="v">
    <xsl:key name="kValByOld" match="v:v" use="@o"/>
    <xsl:key name="kVarByReplace"
             match="var"
             use="document('')/*/v:v[@o=current()/@value]/@n"/>
    <xsl:variable name="vStylesheet" select="document('')"/>
    <v:v o="a-value" n="new-a-value"/>
    <v:v o="b-value" n="new-b-and-c-value"/>
    <v:v o="c-value" n="new-b-and-c-value"/>
    <v:v o="d-value" n="new-d-value"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="var">
        <xsl:variable name="vOld" select="@value"/>
        <xsl:for-each select="$vStylesheet">
            <xsl:variable name="vNew"
                          select="key('kValByOld',$vOld)/@n"/>
            <xsl:for-each select="$vOld">
                <xsl:if test="count(..|key('kVarByReplace',$vNew)[1])=1">
                    <var value="{$vNew}">
                        <xsl:value-of select="$vNew"/>
                    </var>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
2 голосов
/ 03 февраля 2011

Вот таблица стилей XSLT 2.0, которая производит желаемый вывод из вашего ввода. Правильно ли он работает с другими входными данными - это другой вопрос, но он разработан для того, чтобы быть соответствующим образом расширяемым и демонстрировать множество методов, которые можно использовать для решения этой проблемы:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://local-functions/"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="f xs">

<xsl:output indent="yes"/>

<xsl:template match="variables">
  <variable>
     <xsl:for-each-group select="*" group-adjacent="f:is-groupable(.)">
        <xsl:choose>
           <xsl:when test="f:is-groupable(.)">
              <var value="new-{string-join(current-group()/replace(@value, '-value', ''), '-and-')}-value">
                 <xsl:text> new-</xsl:text>
                 <xsl:value-of select="string-join(current-group()/normalize-space(.), '-and-')"/>
                 <xsl:text> </xsl:text>
              </var>
           </xsl:when>
           <xsl:otherwise>
              <xsl:for-each select="current-group()">
                <var value="new-{@value}">
                   <xsl:value-of select="replace(., '\S+', 'new-$0')"/>
                </var>
              </xsl:for-each>      
           </xsl:otherwise>
        </xsl:choose>
     </xsl:for-each-group>
  </variable>
</xsl:template>

<xsl:function name="f:is-groupable" as="xs:boolean">
    <xsl:param name="var" as="element(var)"/>
    <xsl:sequence select="$var/@value = ('b-value', 'c-value')"/>
</xsl:function>     

</xsl:stylesheet>
1 голос
/ 03 февраля 2011

Относительно «запустить другую итерацию в середине обработанного XML, который удалит дубликаты» - это возможно в XSLT 2.0 или при наличии расширения набора узлов в XSLT 1.0. Но я не думаю, что это необходимо в этом случае.

Вместо «для каждого» и «выбирай, когда проверяешь», в противном случае примените шаблоны:

<xsl:apply-templates select="var" />

Затем у вас есть шаблон для каждого типа переменной, который вы хотите обработать, примерно соответствующий вашему xsl: when elements:

<xsl:template match="var[@value = 'a-value']">
    <var value="new-a-value"> new-a </var>
</xsl:template>

А для ч / б предметов что-то вроде:

<xsl:template match="var[@value = 'b-value' or @value = 'c-value'][1]">
    <var value="new-b-and-c-value"> new-b-and-c </var>
</xsl:template>

[1] запускает шаблон только для первой такой переменной среди своих братьев и сестер. Будет ли это именно то, что вы хотите, зависит от того, как может измениться структура вашего ввода. Но учитывая ваш пример ввода, это работает. Вам также понадобится шаблон для обработки переменных b или c, которые не являются первыми:

<xsl:template match="var[@value = 'b-value' or @value = 'c-value']"
        priority="-1" />

Обратите внимание на пустое тело; и низкий приоритет, так что переменные b / c, которые являются первыми, не будут обработаны этим шаблоном.

Подводя итог, учитывая ваш пример ввода, следующая таблица стилей XSLT дает запрашиваемый вывод:

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

   <xsl:template match="/variables">
      <xsl:copy>
         <xsl:apply-templates select="var"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="var[@value = 'a-value']">
      <var value="new-a-value"> new-a </var>
   </xsl:template>

   <xsl:template match="var[@value = 'b-value' or @value = 'c-value'][1]">
      <var value="new-b-and-c-value"> new-b-and-c </var>
   </xsl:template>

   <xsl:template match="var[@value = 'b-value' or @value = 'c-value']"
        priority="-1" />

   <xsl:template match="var[@value = 'd-value']">
      <var value="new-d-value"> new-d </var>
   </xsl:template>
</xsl:stylesheet>

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

Если вы знаете, что на входе будет только один «b», вы можете упростить его и просто иметь шаблон, соответствующий «b», и заменить его новым значением; и иметь пустой шаблон, чтобы соответствовать 'c' и выбросить его. Но это звучало так, как будто ты не можешь сделать такое предположение ...

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