Объединение элементов из 2 списков в XSLT - PullRequest
1 голос
/ 13 декабря 2010

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

<biscuit>
 <name>Hobnobs</name>
 <price>1.49</price>
 <name>Digestives</name>
 <price>89.00</price>
</biscuit>


 <biscuitInfo name="Hobnobs">
  <nutritionalValue>
     <fat>6 grams</fat>
     <sugar>lots</sugar>
   </nutritionalValue>      
  </biscuitInfo>
  <biscuitInfo name="Digestives">
   <nutritionalValue>
     <fat>3 grams</fat>
     <sugar>5 grams</sugar>
   </nutritionalValue>
 </biscuitInfo>

И я хотел бы использовать XSLT, чтобы превратить его в нечто, похожее на:

<biscuit>
     <name>Hobnobs</name>
     <price>1.49</price>
     <fat>6 grams</fat>
     <sugar>lots</sugar>
</biscuit>

Как мне поступить так в XSLT?Могу ли я пройтись по первому списку печенья (название и цена) и извлечь элементы из второго списка (пищевая ценность)?

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

Приветствия,

JD.

Ответы [ 4 ]

2 голосов
/ 13 декабря 2010

Два примера. Эта таблица стилей использует классическую полную рекурсию:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kNutChildByBisName" match="nutritionalValue/*"
             use="../../@name"/>
    <xsl:key name="kElemByPrecedingName" match="biscuit/*[not(self::name)]"
             use="preceding-sibling::name[1]"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="name" mode="group">
        <bisquit>
            <xsl:apply-templates select=".|key('kNutChildByBisName',.)|
                                         key('kElemByPrecedingName',.)"/>
        </bisquit>
    </xsl:template>
    <xsl:template match="biscuit">
        <xsl:apply-templates mode="group"/>
    </xsl:template>
    <xsl:template match="biscuitInfo"/>
    <xsl:template match="node()" mode="group"/>
</xsl:stylesheet>

И эта таблица стилей использует мелкозернистый обход:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kNutByBisName" match="nutritionalValue"
                 use="../@name"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="biscuitInfo"/>
    <xsl:template match="biscuit">
        <xsl:apply-templates select="node()[1]|following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="name[1]" name="group">
        <bisquit>
            <xsl:call-template name="identity"/>
            <xsl:apply-templates select="key('kNutByBisName',.)/node()[1]"/>
        </bisquit>
        <xsl:apply-templates select="following-sibling::name[1]" mode="group"/>
    </xsl:template>
    <xsl:template match="name"/>
    <xsl:template match="name" mode="group">
        <xsl:call-template name="group"/>
    </xsl:template>
</xsl:stylesheet>

С этим входом:

<root>
    <biscuit>
        <name>Hobnobs</name>
        <price>1.49</price>
        <name>Digestives</name>
        <price>89.00</price>
    </biscuit>
    <biscuitInfo name="Hobnobs">
        <nutritionalValue>
            <fat>6 grams</fat>
            <sugar>lots</sugar>
        </nutritionalValue>
    </biscuitInfo>
    <biscuitInfo name="Digestives">
        <nutritionalValue>
            <fat>3 grams</fat>
            <sugar>5 grams</sugar>
        </nutritionalValue>
    </biscuitInfo>
</root>

Оба выхода:

<root>
    <bisquit>
        <name>Hobnobs</name>
        <price>1.49</price>
        <fat>6 grams</fat>
        <sugar>lots</sugar>
    </bisquit>
    <bisquit>
        <name>Digestives</name>
        <price>89.00</price>
        <fat>3 grams</fat>
        <sugar>5 grams</sugar>
    </bisquit>
</root>

Примечание : вы выполняете две задачи: группирование и перекрестные ссылки.

Редактировать : лучший мелкозернистый обход, если в группе только name.

1 голос
/ 13 декабря 2010

Традиционное правило идентификации, переопределенное несколькими шаблонами .Нет ключей, нет детального обхода:

<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()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="biscuit/name">
  <biscuit>
    <xsl:call-template name="identity"/>
    <xsl:apply-templates select=
    "following-sibling::price[1]
    |
      ../../biscuitInfo[@name = current()]/*"/>
  </biscuit>
 </xsl:template>

 <xsl:template match="biscuit">
  <xsl:apply-templates select="name"/>
 </xsl:template>

 <xsl:template match="nutritionalValue">
   <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="biscuitInfo"/>
</xsl:stylesheet>

При применении к этому источнику XML (по сути, предоставленный, но заключенный в один верхний элемент):

<product>
    <biscuit>
        <name>Hobnobs</name>
        <price>1.49</price>
        <name>Digestives</name>
        <price>89.00</price>
    </biscuit>
    <biscuitInfo name="Hobnobs">
        <nutritionalValue>
            <fat>6 grams</fat>
            <sugar>lots</sugar>
        </nutritionalValue>
    </biscuitInfo>
    <biscuitInfo name="Digestives">
        <nutritionalValue>
            <fat>3 grams</fat>
            <sugar>5 grams</sugar>
        </nutritionalValue>
    </biscuitInfo>
</product>

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

<product>
   <biscuit>
      <name>Hobnobs</name>
      <price>1.49</price>
      <fat>6 grams</fat>
      <sugar>lots</sugar>
   </biscuit>
   <biscuit>
      <name>Digestives</name>
      <price>89.00</price>
      <fat>3 grams</fat>
      <sugar>5 grams</sugar>
   </biscuit>
</product>

Примечание :

  1. Использование и переопределение правила идентификации является фундаментальным шаблоном проектирования XSLT , и его использование облегчает написание практически любого преобразования.

  2. Использование <xsl:apply-templates/> вместо <xsl:apply-templates select="node()[1]"/> допускает параллельное выполнение , что может стать все более важным в ближайшем будущем.

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

1 голос
/ 13 декабря 2010

Это XSLT:

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

<xsl:key name="bisquit-info" match="/root/biscuitInfo" use="@name"/>

<xsl:template match="root">
    <xsl:copy>
        <xsl:apply-templates select="biscuit/name"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="name">
    <bisquit>
        <xsl:copy-of select="."/>
        <xsl:copy-of select="following-sibling::price[1]"/>
        <xsl:copy-of select="key('bisquit-info', .)/nutritionalValue/*"/>
    </bisquit>
</xsl:template> 
</xsl:stylesheet>

С таким вводом XML (только что добавленный корневой узел для корректности)

<root>
<biscuit>
    <name>Hobnobs</name>
    <price>1.49</price>
    <name>Digestives</name>
    <price>89.00</price>
</biscuit>

<biscuitInfo name="Hobnobs">
    <nutritionalValue>
    <fat>6 grams</fat>
    <sugar>lots</sugar>
    </nutritionalValue>      
</biscuitInfo>
<biscuitInfo name="Digestives">
    <nutritionalValue>
    <fat>3 grams</fat>
    <sugar>5 grams</sugar>
    </nutritionalValue>
</biscuitInfo>
</root>

Будет обеспечивать такой результат:

<root>
<bisquit>
    <name>Hobnobs</name>
    <price>1.49</price>
    <fat>6 grams</fat>
    <sugar>lots</sugar>
</bisquit>
<bisquit>
    <name>Digestives</name>
    <price>89.00</price>
    <fat>3 grams</fat>
    <sugar>5 grams</sugar>
</bisquit>
</root>

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

1 голос
/ 13 декабря 2010

Что вы можете сделать, это использовать XPath для второй части. Выполните цикл в вашем первом списке, и как только у вас появится имя, используйте XPath для запроса именно того, что вы хотите во втором списке.

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