Повторное использование шаблона XSL для вызова с различными относительными XPath - PullRequest
0 голосов
/ 18 июня 2010

Вот мой надуманный пример, который иллюстрирует то, что я пытаюсь достичь.У меня есть входной XML-файл, который я хочу сгладить для дальнейшей обработки.

Входной файл:

<BICYCLES>
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEELS>
            <WHEEL>
                <WHEEL_TYPE>FRONT</WHEEL_TYPE>
                <FLAT>NO</FLAT>
                <REFLECTORS>
                    <REFLECTOR>
                        <REFLECTOR_NUM>1</REFLECTOR_NUM>
                        <SHAPE>SQUARE</SHAPE>
                    </REFLECTOR>
                    <REFLECTOR>
                        <REFLECTOR_NUM>2</REFLECTOR_NUM>
                        <SHAPE>ROUND</SHAPE>
                    </REFLECTOR>
                </REFLECTORS>
            </WHEEL>
            <WHEEL>
                <WHEEL_TYPE>REAR</WHEEL_TYPE>
                <FLAT>NO</FLAT>
            </WHEEL>
        </WHEELS>
    </BICYCLE>
</BICYCLES>

Входные данные представляют собой список <BICYCLE> узлов.Каждый <BICYCLE> имеет <COLOR> и опционально имеет <WHEELS>.

<WHEELS> - это список <WHEEL> узлов, каждый из которых имеет несколько атрибутов и, необязательно, имеет <REFLECTORS>.

<REFLECTORS> - это список <REFLECTOR> узловкаждый из которых имеет несколько атрибутов.

Цель состоит в том, чтобы сгладить этот XML.Это XSL, который я использую:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" xml:space="preserve"/> 

<xsl:template match="/">
<BICYCLES>
<xsl:apply-templates/>
</BICYCLES>
</xsl:template>

<xsl:template match="BICYCLE">
<xsl:choose>
    <xsl:when test="WHEELS">
        <xsl:apply-templates select="WHEELS"/>
    </xsl:when>
    <xsl:otherwise>
        <BICYCLE>
            <COLOR><xsl:value-of select="COLOR"/></COLOR>
            <WHEEL_TYPE/>
            <FLAT/>
            <REFLECTOR_NUM/>
            <SHAPE/>
        </BICYCLE>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="WHEELS">
<xsl:apply-templates select="WHEEL"/>
</xsl:template>

<xsl:template match="WHEEL">
    <xsl:choose>
        <xsl:when test="REFLECTORS">
            <xsl:apply-templates select="REFLECTORS"/>
        </xsl:when>
        <xsl:otherwise>
            <BICYCLE>
                <COLOR><xsl:value-of select="../../COLOR"/></COLOR>
                <WHEEL_TYPE><xsl:value-of select="WHEEL_TYPE"/></WHEEL_TYPE>
                <FLAT><xsl:value-of select="FLAT"/></FLAT>
                <REFLECTOR_NUM/>
                <SHAPE/>
            </BICYCLE>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="REFLECTORS">
    <xsl:apply-templates select="REFLECTOR"/>
</xsl:template>

<xsl:template match="REFLECTOR"> 
    <BICYCLE>
        <COLOR><xsl:value-of select="../../../../COLOR"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="../../WHEEL_TYPE"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="../../FLAT"/></FLAT>
        <REFLECTOR_NUM><xsl:value-of select="REFLECTOR_NUM"/></REFLECTOR_NUM>
        <SHAPE><xsl:value-of select="SHAPE"/></SHAPE>
    </BICYCLE>
</xsl:template>

</xsl:stylesheet>

Вывод:

<BICYCLES xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEEL_TYPE>FRONT</WHEEL_TYPE>
        <FLAT>NO</FLAT>
        <REFLECTOR_NUM>1</REFLECTOR_NUM>
        <SHAPE>SQUARE</SHAPE>
    </BICYCLE>
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEEL_TYPE>FRONT</WHEEL_TYPE>
        <FLAT>NO</FLAT>
        <REFLECTOR_NUM>2</REFLECTOR_NUM>
        <SHAPE>ROUND</SHAPE>
    </BICYCLE>
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEEL_TYPE>REAR</WHEEL_TYPE>
        <FLAT>NO</FLAT>
        <REFLECTOR_NUM/>
        <SHAPE/>
    </BICYCLE>
</BICYCLES>

Что мне не нравится в этом, так это то, что я выводю атрибут цвета в несколькихформы:

<COLOR><xsl:value-of select="../../../../COLOR"/></COLOR>
<COLOR><xsl:value-of select="../../COLOR"/></COLOR>
<COLOR><xsl:value-of select="COLOR"/></COLOR>
<COLOR/>

Кажется, должен быть способ создать именованный шаблон и вызывать его из различных мест, где он необходим, и передавать какой-либо параметр, представляющий путь, обратно к <BICYCLE>узел, к которому он относится.

Есть ли способ убрать это, скажем, с помощью именованного шаблона для велосипедных полей, для колесных полей и для отражательных полей?основано на том, что у «велосипеда» гораздо больше атрибутов, чем просто цвет, и я хочу, чтобы этот XSL легко изменялся для включения или исключения полей без необходимости изменять XSL в нескольких местах.

Ответы [ 5 ]

2 голосов
/ 18 июня 2010

Что мне не нравится в этом, так это то, что Я вывожу атрибут цвета в несколько форм:

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

Есть ли способ убрать это, скажем, с названным шаблоном для велосипеда поля, для колесных полей и для поля отражателя?

Вы можете добиться еще большего повторного использования, чем этот .

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

Следующая трансформация демонстрирует эти принципы :

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

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

 <xsl:template match="BICYCLE">
  <xsl:apply-templates>
    <xsl:with-param name="pColor" select="COLOR"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="BICYCLE[not(WHEELS)]">
    <BICYCLE>
        <COLOR><xsl:value-of select="COLOR"/></COLOR>
        <WHEEL_TYPE/>
        <FLAT/>
        <REFLECTOR_NUM/>
        <COLOR/>
        <SHAPE/>
    </BICYCLE>
 </xsl:template>

 <xsl:template match="WHEELS">
   <xsl:param name="pColor"/>

   <xsl:apply-templates>
    <xsl:with-param name="pColor" select="$pColor"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="WHEEL[REFLECTORS]">
   <xsl:param name="pColor"/>

   <xsl:apply-templates select="REFLECTORS">
    <xsl:with-param name="pColor" select="$pColor"/>
    <xsl:with-param name="pWheel_Type" select="WHEEL_TYPE"/>
    <xsl:with-param name="pFlat" select="FLAT"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="WHEEL">
   <xsl:param name="pColor"/>

    <BICYCLE>
        <COLOR><xsl:value-of select="$pColor"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="WHEEL_TYPE"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="FLAT"/></FLAT>
        <REFLECTOR_NUM/>
        <COLOR/>
        <SHAPE/>
    </BICYCLE>
 </xsl:template>

 <xsl:template match="REFLECTORS">
  <xsl:param name="pColor"/>
  <xsl:param name="pWheel_Type"/>
  <xsl:param name="pFlat"/>

  <xsl:apply-templates select="REFLECTOR">
    <xsl:with-param name="pColor" select="$pColor"/>
    <xsl:with-param name="pWheel_Type" select="$pWheel_Type"/>
    <xsl:with-param name="pFlat" select="$pFlat"/>
  </xsl:apply-templates>
</xsl:template>

 <xsl:template match="REFLECTOR">
  <xsl:param name="pColor"/>
  <xsl:param name="pWheel_Type"/>
  <xsl:param name="pFlat"/>

    <BICYCLE>
        <COLOR><xsl:value-of select="$pColor"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="$pWheel_Type"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="$pFlat"/></FLAT>
        <REFLECTOR_NUM><xsl:value-of select="REFLECTOR_NUM"/></REFLECTOR_NUM>
        <COLOR><xsl:value-of select="COLOR"/></COLOR>
        <SHAPE><xsl:value-of select="SHAPE"/></SHAPE>
    </BICYCLE>
 </xsl:template>

 <xsl:template match="BICYCLE/COLOR"/>
</xsl:stylesheet>

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

<BICYCLES>
 <BICYCLE>
  <COLOR>BLUE</COLOR>
  <WHEEL_TYPE>FRONT</WHEEL_TYPE>
  <FLAT>NO</FLAT>
  <REFLECTOR_NUM>1</REFLECTOR_NUM>
  <COLOR>RED</COLOR>
  <SHAPE>SQUARE</SHAPE>
 </BICYCLE>
 <BICYCLE>
  <COLOR>BLUE</COLOR>
  <WHEEL_TYPE>FRONT</WHEEL_TYPE>
  <FLAT>NO</FLAT>
  <REFLECTOR_NUM>2</REFLECTOR_NUM>
  <COLOR>WHITE</COLOR>
  <SHAPE>ROUND</SHAPE>
 </BICYCLE>
 <BICYCLE>
  <COLOR>BLUE</COLOR>
  <WHEEL_TYPE>REAR</WHEEL_TYPE>
  <FLAT>NO</FLAT>
  <REFLECTOR_NUM/>
  <COLOR/>
  <SHAPE/>
 </BICYCLE>
</BICYCLES>
2 голосов
/ 18 июня 2010

Вы можете именовать шаблоны, используя атрибут name. Вы вызываете шаблон по имени, используя <xsl:call-template>, и он действителен (IIRC) везде, где <xsl:apply-templates> допустим.

ОБНОВЛЕНИЕ (из комментариев): Звучит так, будто вы хотите другую ось , вероятно ancestor. Что-то вроде ancestor::bicycle/color?

0 голосов
/ 18 июня 2010

Два способа.

Сохранение использования оси ancestor-or-self:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:template match="/"> 
    <BICYCLES> 
        <xsl:for-each select="BICYCLE[not(WHELLS)]|//REFLECTOR|//WHEEL[not(REFLECTORS)]"> 
            <BICYCLE> 
                <COLOR><xsl:value-of select="ancestor-or-self::BICYCLE/COLOR"/></COLOR> 
                <WHEEL_TYPE><xsl:value-of select="ancestor-or-self::WHEEL/WHEEL_TYPE"/></WHEEL_TYPE> 
                <FLAT><xsl:value-of select="ancestor-or-self::WHEEL/FLAT"/></FLAT> 
                <REFLECTOR_NUM><xsl:value-of select="ancestor-or-self::REFLECTOR/REFLECTOR_NUM"/></REFLECTOR_NUM> 
                <COLOR><xsl:value-of select="ancestor-or-self::REFLECTOR/COLOR"/></COLOR> 
                <SHAPE><xsl:value-of select="ancestor-or-self::REFLECTOR/SHAPE"/></SHAPE> 
            </BICYCLE> 
        </xsl:for-each> 
    </BICYCLES> 
</xsl:template> 

</xsl:stylesheet> 

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

Напротив, с помощью поста Dimitre вы можете избежать условной логики и применить шаблон к желаемой ветви.

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output indent="yes" method="xml"/>  

    <xsl:template match="/"> 
        <BICYCLES> 
            <xsl:apply-templates/> 
        </BICYCLES> 
    </xsl:template>  

    <!--
        When you don't want a leaf in your result, just:
        <xsl:template match="here the leaf name" /> 
    --> 

    <xsl:template match="*"> 
        <xsl:copy> 
            <xsl:apply-templates/> 
        </xsl:copy> 
    </xsl:template>  

    <xsl:template match="*[*/*]"> 
        <xsl:param name="prev" /> 
        <xsl:apply-templates select="*[*]">
            <xsl:with-param name="prev">
                <xsl:copy-of select="$prev"/>
                <xsl:apply-templates select="*[not(*)]"/>
            </xsl:with-param>
        </xsl:apply-templates> 
    </xsl:template>  

    <xsl:template match="*[* and not(*/*)]"> 
        <xsl:param name="prev" /> 
        <BICYCLE>
            <xsl:copy-of select="$prev"/>
            <xsl:apply-templates select="*"/>
        </BICYCLE>
    </xsl:template>  

</xsl:stylesheet> 

Примечание: вам не нужно знать, какиелист.

Редактировать : худший пример использования осей потомков и предков и некоторые изменения в моем предложении

Edit2 : добавление нового возможноголист (я пропустил это в первый раз!) для худшей реализации.Добавление возможности исключить некоторые листы для второй реализации.

Edit3 : просто переписать, чтобы прояснить мою точку зрения.

0 голосов
/ 18 июня 2010

Спасибо, Хэнк Гей! Это то, что я искал. Следующий XSL имеет тот же вывод, что и мой оригинал:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" xml:space="preserve"/> 

<xsl:template name="allfields">
    <BICYCLE>
        <COLOR><xsl:value-of select="ancestor-or-self::BICYCLE/COLOR"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="ancestor-or-self::WHEEL/WHEEL_TYPE"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="ancestor-or-self::WHEEL/FLAT"/></FLAT>
        <REFLECTOR_NUM><xsl:value-of select="ancestor-or-self::REFLECTOR/REFLECTOR_NUM"/></REFLECTOR_NUM>
        <SHAPE><xsl:value-of select="ancestor-or-self::REFLECTOR/SHAPE"/></SHAPE>
    </BICYCLE>
</xsl:template>

<xsl:template match="/">
<BICYCLES>
    <xsl:apply-templates/>
</BICYCLES>
</xsl:template>

<xsl:template match="BICYCLE">
<xsl:choose>
    <xsl:when test="WHEELS">
        <xsl:apply-templates/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:call-template name="allfields"/>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="WHEEL">
            <xsl:apply-templates/>
    <xsl:choose>
        <xsl:when test="REFLECTORS">
            <xsl:apply-templates/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="allfields"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="REFLECTOR"> 
    <xsl:call-template name="allfields"/>
</xsl:template>

</xsl:stylesheet>
0 голосов
/ 18 июня 2010

Вы имеете в виду, как:

<xsl:template name="wheelblock">
  <xsl:param name="color"></xsl:param>
  <!-- Do something here -->
</xsl:template>

<xsl:template match="WHEEL">
  <xsl:call-template name="wheelblock">
    <xsl:with-param name="color">whatever element/etc</xsl:with-param>
  </xsl:call-template>
</xsl:template>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...