2009-02-11 4 views
4

Я пытаюсь сделать преобразование XSLT, который генерирует код C, следующий XML должен быть преобразован:Как эмулировать C перечисление в XSLT с дополнительными значениями

<enum name="anenum"> 
    <enumValue name="a"/> 
    <enumValue name="b"/> 
    <enumValue name="c" data="10"/> 
    <enumValue name="d" /> 
    <enumValue name="e" /> 
</enum> 

Он должен преобразовать в некоторый код C следующим образом :

enum anenum { 
    a = 0, 
    b = 1, 
    c = 10, 
    d = 11, 
    e = 12 
} 

или в качестве альтернативы (в качестве препроцессора C будет обрабатывать суммирование):

enum anenum { 
     a = 0, 
     b = 1, 
     c = 10, 
     d = c+1, 
     e = c+2 
    } 

Ядро моей XSLT выглядит следующим образом:

<xsl:for-each select="enumValue"> 
    <xsl:value-of select="name"/> 
    <xsl:text> = </xsl:text> 
    <xsl:choose> 
    <xsl:when test="string-length(@data)&gt;0"> 
     <xsl:value-of select="@data"/> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:value-of select="position()-1"/> 
    </xsl:otherwise> 
    </xsl:choose> 
    <xsl:text>, 

(для простоты я пропустить некоторые «нет запятой в последнем элементе» кода)

Этот пример не будет генерировать правильные значения для д и х

Я пытался получить его работу для переменной г и e, но до сих пор я не увенчался успехом.

Использование конструкции типа:

<xsl:when test="string-length(preceding-sibling::enumValue[1]/@datavalue)&gt;0"> 
    <xsl:value-of select="preceding-sibling::enumValue/@data + 1"/> 
</xsl:when> 

... работать только для первого после указанного значения (в данном случае г).

Кто может мне помочь? Я, вероятно, слишком много размышляю в процедурной форме ...

+0

Я улучшил решение, которое использует ключи, и теперь это кажется действительно хорошим, см. Мой новый ответ. :) –

+0

@Roalt Но если что-то отмечено «wiki сообщества», это мешает ему получать дальнейшие голоса - это нехорошо. –

+0

@Roalt Но последний ответ также реализован. И это работает. Или что вы подразумеваете под ", но я использую ваш предыдущий ответ, потому что (1) он реализован, и он работает" –

ответ

3

Нерекурсивный раствор, с помощью клавиш:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:strip-space elements="*"/> 
<xsl:output method="text"/> 

<xsl:key name="koffsetEnums" match="enumValue[@data]" 
    use="generate-id()"/> 

    <xsl:template match="enum"> 
     enum <xsl:value-of select="@name"/> { 
     <xsl:apply-templates select="enumValue"/> 
     } 
    </xsl:template> 

    <xsl:template match="enumValue"> 
     <xsl:value-of select="concat(@name, ' = ')"/> 

     <xsl:variable name="voffsetValueId" select= 
     "generate-id((. | preceding-sibling::enumValue) 
              [@data][last()] 
       )"/> 

     <xsl:choose> 
     <xsl:when test="not($voffsetValueId)"> 
      <xsl:value-of select="concat(position(),'&#xA;  ')"/> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:variable name="vinitOffset" select= 
      "key('koffsetEnums', $voffsetValueId)/@data" 
      /> 

      <xsl:value-of select= 
      "$vinitOffset 
      + 
       count(preceding-sibling::enumValue) 
      - 
       count(key('koffsetEnums', $voffsetValueId)/preceding-sibling::enumValue) 
      " 
      /> 
      <xsl:text>&#xA;  </xsl:text> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

</xsl:stylesheet> 

Когда выше преобразование применяется на первоначально предоставленной XML документа:

<enum name="anenum"> 
    <enumValue name="a"/> 
    <enumValue name="b"/> 
    <enumValue name="c" data="10"/> 
    <enumValue name="d" /> 
    <enumValue name="e" /> 
</enum> 

требуемый результат произведено:

enum anenum { 
     a = 1 
     b = 2 
     c = 10 
     d = 11 
     e = 12 

     } 
+0

Действительно как этот! –

+0

Ничего себе, этот тоже приятный! Я также рассматривал ключи, но не знал, как управлять ими! – Roalt

+0

@Roalt Я улучшил решение, и теперь это кажется действительно хорошим, см. Мой новый ответ. :) –

0

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

1

Вы не можете изменить «переменные» в xsl, но вы можете использовать рекурсию. Не используйте предикаты предшествовавших братьев, если они не являются абсолютно безотлагательными, поскольку они убьют вашу работу.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> 
    <xsl:template match="/" > 
     <xsl:call-template name="printEnum"> 
      <xsl:with-param name="value" select="0"/> 
      <xsl:with-param name="position" select="1"/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="printEnum"> 
     <xsl:param name="position"/>  
     <xsl:param name="value" select="0"/> 
     <xsl:variable name="node" select="/enum/enumValue[$position]"/> 
     <xsl:variable name="enumValue"> 
      <xsl:choose> 
       <xsl:when test="$node/@data"> 
        <xsl:value-of select="$node/@data"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="$value + 1"/> 
       </xsl:otherwise> 
      </xsl:choose>  
     </xsl:variable>  
     <xsl:value-of select="concat($node/@name, ' = ', $enumValue, ' , ')"/> 
     <xsl:if test="/enum/enumValue[$position + 1]"> 
      <xsl:call-template name="printEnum"> 
       <xsl:with-param name="value" select="$enumValue"/> 
       <xsl:with-param name="position" select="$position + 1"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 
+0

Спасибо! Тем не менее, вам необходимо изменить в последнем шаблоне вызова строку в , чтобы он работал. – Roalt

+0

Исправлено: - Рад, что это работает – Goran

2

Лучшее решение с ключами, избегая максимально использовать предыдущий-собрата ось:

Это преобразование:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:output method="text"/> 
<!--            --> 
    <xsl:key name="ksimpleEnValues" match="enumValue[not(@data)]" 
    use="generate-id(preceding-sibling::enumValue[@data][1])"/> 
<!--            -->  
    <xsl:template match="enum"> 
    enum <xsl:value-of select="@name"/> 
     {  
      <xsl:apply-templates select= 
      "key('ksimpleEnValues', '') 
      "/> 
      <xsl:apply-templates select="enumValue[@data]"/> 
     } 
    </xsl:template> 
<!--            --> 
    <xsl:template match="enumValue"> 
     <xsl:param name="pOffset" select="0"/> 
     <xsl:value-of select= 
     "concat(@name, ' = ', position()+$pOffset,'&#xA;  ')"/> 
    </xsl:template> 
<!--            --> 
    <xsl:template match="enumValue[@data]"> 
     <xsl:value-of select= 
     "concat(@name, ' = ', @data,'&#xA;  ')"/> 
<!--            --> 
     <xsl:apply-templates select= 
      "key('ksimpleEnValues', generate-id())"> 
     <xsl:with-param name="pOffset" select="@data"/> 
     </xsl:apply-templates> 
    </xsl:template>  
</xsl:stylesheet> 

при нанесении на первоначально предоставленного XML документ:

<enum name="anenum"> 
    <enumValue name="a"/> 
    <enumValue name="b"/> 
    <enumValue name="c" data="10"/> 
    <enumValue name="d" /> 
    <enumValue name="e" /> 
</enum> 

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

enum anenum 
{  
    a = 1 
    b = 2 
    c = 10 
    d = 11 
    e = 12 
} 

Пояснения:

  1. Ключ имени ksimpleEnValues индексирует все enumValue элементов, которые не имеют атрибута data. Индексирование выполняется с помощью значения generate-id() первого предшествующего элемента enumValue, который имеет атрибут data.

  2. Таким образом, key('ksimpleEnValues', someId) является набор узлов, содержащий все элементы enumValue после enumValue, который имеет свой идентификатор генерировать-(), равный someId, и все эти элементы enumValue предшествующий следующий enumValue с атрибутом data, если таковой существует.

  3. ключ («ksimpleEnValues», «») представляет собой набор узлов всех enumValue элементов, которые не имеют предшествующую enumValue элемента с атрибутом data.

  4. Шаблон, который соответствует enumValue принимает необязательный параметр $pOffset, в котором значение атрибута data от непосредственного предыдущего enumValue элемента с этим атрибутом, будет принято, в противном случае значение по умолчанию равно 0. $pOffset

  5. шаблон согласования enumValue элементы, которые имеют атрибут data производит его перечислимую-значение (@name = @data), а затем применяет шаблоны для всех enumValue элементов между собой и следующим (если такой существует) enumValue с атрибутом data.Значение атрибута data передается как параметр $pOffset, и оно будет добавлено в относительное положение каждого выбранного элемента enumValue при получении результата обработки.

+0

Если кто-нибудь может объяснить, почему этот ответ стал «вики-сообществом», я был бы очень признателен. ?!? –

+0

Я поставил флаг там, когда принял первый/начальный ответ. – Roalt

+0

Я думаю, что это улучшение, вероятно, лучше, чем ваш предыдущий ответ, но я использую ваш предыдущий ответ, потому что (1) он реализован и работает, и (2) производительность не является проблемой. – Roalt

Смежные вопросы