Как переформатировать XML со связанными группами элементов, используя XSLT - PullRequest
4 голосов
/ 31 января 2012

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

    <rules>
        <if condition="equals" arg1="somevar" arg2="1"/>
        <elseif condition="equals" arg1="somevar" arg2="2"/>
        <elseif condition="equals" arg1="somevar" arg2="3"/>
        <else/>
        <if condition="equals" arg1="somevar" arg2="4"/>
        <else/>
    </rules>

Кажется, что это трудно проверить с помощью XSD, поэтому я хотел бы преобразовать его в нечто вроде этого - идеи?

    <rules>
        <conditionSet>
            <if condition="equals" arg1="somevar" arg2="1"/>
            <elseif condition="equals" arg1="somevar" arg2="2"/>
            <elseif condition="equals" arg1="somevar" arg2="3"/>
            <else/>
        </conditionSet>
        <conditionSet>
            <if condition="equals" arg1="somevar" arg2="4"/>
            <else/>
        </conditionSet>
    </rules>

Ответы [ 4 ]

1 голос
/ 17 августа 2012

Это интересный вызов XSLT. Но почему вы снова меняете XML? Шаблон на входе может быть легко определен с помощью регулярного выражения, а именно

(if, elseif*, else)*

и по этой причине его легко проверить с помощью XSD.

Возможно, стоит поменяться - ветеран-дизайнер словарного запаса (Линн А. Прайс) однажды сказал мне, что любой оператор повторения в группе автоматически подозревается и часто означает, что группу следует заменить элементом. Думаю, она одобрит твои изменения. Но чтобы иметь смысл, обоснование изменений должно заключаться в большей простоте обработки, а не в простой проверке.

1 голос
/ 01 февраля 2012

Группа elseif и else элементов по их непосредственно предшествующим элементу if:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:key name="block" match="elseif|else" 
             use="generate-id(preceding-sibling::if[1])"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="rules">
        <xsl:copy>
            <xsl:apply-templates select="@*|
                    node()[not(self::elseif or self::else)]"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="if">
        <conditionSet>
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
            <xsl:apply-templates select="key('block', generate-id())"/>
        </conditionSet>
    </xsl:template>
</xsl:stylesheet>

Эта таблица стилей создает запрошенный вывод.

Объяснение: xsl:key связывает каждый элемент if с его следующими связанными элементами, так что позже, когда мы сопоставим if, мы можем просто обернуть и скопировать весь набор.

0 голосов
/ 01 февраля 2012

I. Намного проще и короче решение XSLT 1.0 :

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

 <xsl:key name="kFollowing" match="elseif|else"
  use="generate-id(preceding-sibling::if[1])"/>

 <xsl:template match="/*">
  <rules>
   <xsl:apply-templates select="if"/>
  </rules>
 </xsl:template>

 <xsl:template match="if">
  <conditionSet>
   <xsl:copy-of select=".|key('kFollowing', generate-id())"/>
  </conditionSet>
 </xsl:template>
</xsl:stylesheet>

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

<rules>
    <if condition="equals" arg1="somevar" arg2="1"/>
    <elseif condition="equals" arg1="somevar" arg2="2"/>
    <elseif condition="equals" arg1="somevar" arg2="3"/>
    <else/>
    <if condition="equals" arg1="somevar" arg2="4"/>
    <else/>
</rules>

желаемый, правильный результат получается :

<rules>
   <conditionSet>
      <if condition="equals" arg1="somevar" arg2="1"/>
      <elseif condition="equals" arg1="somevar" arg2="2"/>
      <elseif condition="equals" arg1="somevar" arg2="3"/>
      <else/>
   </conditionSet>
   <conditionSet>
      <if condition="equals" arg1="somevar" arg2="4"/>
      <else/>
   </conditionSet>
</rules>

II. Еще более простое и короткое решение XSLT 2.0 :

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <rules>
   <xsl:for-each-group select="*" group-starting-with="if">
    <conditionSet>
     <xsl:sequence select="current-group()"/>
    </conditionSet>
   </xsl:for-each-group>
  </rules>
 </xsl:template>
</xsl:stylesheet>

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

<rules>
   <conditionSet>
      <if condition="equals" arg1="somevar" arg2="1"/>
      <elseif condition="equals" arg1="somevar" arg2="2"/>
      <elseif condition="equals" arg1="somevar" arg2="3"/>
      <else/>
   </conditionSet>
   <conditionSet>
      <if condition="equals" arg1="somevar" arg2="4"/>
      <else/>
   </conditionSet>
</rules>
0 голосов
/ 01 февраля 2012

Еще один:

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

  <xsl:template match="/rules">
    <xsl:copy>
      <xsl:apply-templates select="if"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="if">
    <conditionSet>
      <xsl:copy-of select="."/>
        <xsl:apply-templates select="
                             following-sibling::*[not(self::if) 
                             and generate-id(preceding-sibling::if[1]) 
                                = generate-id(current())]
                             "/>
    </conditionSet>
  </xsl:template>

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

</xsl:stylesheet>
...