XSLT - 如何根据来自外部 XML 的数据过滤数据源?

XSLT - how can I filter data source by data from external XML?

我有以下 XML 数据结构:

<root>
    <info>
      <creationDate>2015-03-11 11:45:49</creationDate>
    </info>
    <promotions>
      <promotion>
        <header>
          <id>1</id>
          <name>Name 1</name>
        </header>
        <positions>
              <position>
                <id>1</id>
                <name>Some position name 1</name>
              </position>
              <position>
                <id>2</id>
                <name>Some position name 2</name>
              </position>
          </position>
        </positions>
      </promotion>
      <promotion>
        <header>
          <id>2</id>
          <name>Name 2</name>
        </header>
        <positions>
          <position>
            <id>3</id>
            <name>Some position name 3</name>
          </position>
        </positions>
      </promotion>
    </promotions>
  </root>

它用作我的模板的主要数据源,如下所示:

<xsl:template match="root">
...

我需要通过 "filter.xml" 文件 过滤以上内容,其中包含要过滤掉的促销 ID,它需要在 IE7。这样的事情可能吗?

对于初学者,我试图找到一种在 xsl:apply-templates select 语句中添加过滤器的方法,以便 only promotion with Id =2 将由模板处理 但失败了 - 是否可以编写 Xpath 来表示:

"Give me everything from root node but promotions only with Id = 2" ?

谢谢。

编辑 1:

对命名空间感到抱歉 - 它本来就不应该存在。至于 filter.xml - 尚未明确定义 - 目前,我使用以下内容:

<usedUpPromotions>
  <header>
    <promotionId>
      1
    </promotionId>
  </header>
  <header>
    <promotionId>
      2
    </promotionId>
  </header>
  <header>
    <promotionId>
      3
    </promotionId>
  </header>
</usedUpPromotions>

我想使用类似的东西:

<xsl:apply-templates select="root[hereIsMyWhereId != (document('Load externalXmlHere')/select/IdtoFilterOut)"/>

但我似乎无法找到一种方法来过滤掉数据...

编辑 2:

我将尝试使用代码作为示例进行解释 - 让我们暂时假设我们有以下内容:

XmlData initialXmlData; <- 这是我们的 XML 过滤前的数据

XmlData filter; -< 这包含 filter.xml 数据

Html GenerateHtmlFromTemplate(XmlData initialXmlData) - this is my Xslt template
{
...some precessing here
}

我想修改我的模板以实现以下目的:

Html GenerateHtmlFromTemplate(XmlData initialXmlData, XmlData filter) 
{
XmlData filteredData = data.FilterBy(filter);
...same processing here as above, but use 'filteredData', instead of 'initialXmlData'
}

我希望现在更清楚了:) - 主要问题似乎是我要过滤的 Id 元素在数组变量中,所以我不能简单地使用:

在我的主模板中 - 相反,我通过稍后对 for-each 循环进行过滤绕过了这个问题,但我仍然想知道是否可以简单地告诉模板 "from now on use filtered data, instead of original".

编辑 3:

@michael.hor257k - 为了回答你的问题,我修改了你提供的模板:

Template.xml:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="#"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ksx="http://www.test.com/test" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<xsl:output encoding="utf-8" indent="no" method="html"/>

<root xmlns="http://www.test.com/test">
  <info>
    <creationDate>2015-03-11 11:45:49</creationDate>
  </info>
  <promotions>
    <promotion>
      <header>
        <id atr="tre">1</id>
        <name>Promotion 1</name>
      </header>
      <positions>
        <position>
          <id>1</id>
          <name>Position 1a</name>
        </position>
        <position>
          <id>2</id>
          <name>Position 1b</name>
        </position>
      </positions>
    </promotion>
    <promotion>
      <header>
        <id>2</id>
        <name>Promotion 2</name>
      </header>
      <positions>
        <position>
          <id>3</id>
          <name>Position 2a</name>
        </position>
      </positions>
    </promotion>
  </promotions>
</root>

<xsl:param name="new-path" select="'new.xml'"/>
<xsl:variable name="new-promotions" select="document($new-path)/newPromotions/promotion" />



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

<xsl:template match="ksx:root">
    <xsl:copy>
        <xsl:apply-templates select="ksx:promotions/ksx:promotion[not(ksx:header/ksx:id=$new-promotions/header/id)]"/>
        <xsl:apply-templates select="$new-promotions"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

new.xml:

<?xml version="1.0" encoding="utf-8"?>
<newPromotions>
    <promotion>
      <header>
        <id>2</id>
        <name>New Promotion 2</name>
      </header>
      <positions>
        <position>
          <id>4</id>
          <name>New Position 2A</name>
        </position>
      </positions>
    </promotion>
    <promotion>
      <header>
        <id>3</id>
        <name>New Promotion 3</name>
      </header>
      <positions>
        <position>
          <id>5</id>
          <name>New Position 3A</name>
        </position>
      </positions>
    </promotion>
</newPromotions>

如果你保存这些,并在 Chrome 中打开 template.xml 它会很好地工作 - 然而我想要的是过滤掉数据 outside 模板节点:

<xsl:template match="ksx:root">

所以,我尝试了这个:

<xsl:template match="ksx:root[ksx:promotion/ksx:header/ksx:id=1]">

我希望从 root 获取所有数据,但促销过滤到 Id = 1 的那些 - 但它提供了所有数据并且没有错误,现在我注意到这一点:

<xsl:template match="ksx:root[ksx:promotions/ksx:promotion/ksx:header/ksx:id=$new-promotions/header/id]">

导致错误 "Variables cannot be used within this expression" - 所以我想,我想做的事情从模板节点外部可能是不可能的...?

很抱歉造成混淆 - 我希望现在更清楚了。我只是想将模板节点视为一种方法,并 "pass" 向其过滤数据,而不是过滤其中的数据。

编辑4:

在我的具体案例中,我有大型 "root" 促销数据集和小型外部 XML 文件,其中包含要在处理过程中隐藏的促销 ID。所以在我的例子中过滤意味着:"Take everything from root element, but filter promotions so only those which Ids are NOT in the external file, will be processed"。所以如果我有:

<root xmlns="http://www.test.com/test">
  <info>
    <creationDate>2015-03-11 11:45:49</creationDate>
  </info>
  <someData1>Some data 1</someData1>
  <someData2>Some data 2</someData2>
  <someData3>Some data 3</someData3>
  <promotions>
    <promotion>
      <header>
        <id>1</id>
        <name>Promotion 1</name>
      </header>
      <positions>
        <position>
          <id>1</id>
          <name>Position 1a</name>
        </position>
        <position>
          <id>2</id>
          <name>Position 1b</name>
        </position>
      </positions>
    </promotion>
    <promotion>
      <header>
        <id>2</id>
        <name>Promotion 2</name>
      </header>
      <positions>
        <position>
          <id>3</id>
          <name>Position 2a</name>
        </position>
      </positions>
    </promotion>
    <promotion>
      <header>
        <id>3</id>
        <name>Promotion 3</name>
      </header>
      <positions>
        <position>
          <id>4</id>
          <name>Position 3a</name>
        </position>
      </positions>
    </promotion>
  </promotions>
</root>

并过滤:

<?xml version="1.0" encoding="UTF-8"?>
<usedUpPromotions>
    <id>1</id>
    <id>2</id>
</usedUpPromotions>

那么我希望得到:

<root xmlns="http://www.test.com/test">
  <info>
    <creationDate>2015-03-11 11:45:49</creationDate>
  </info>
  <someData1>Some data 1</someData1>
  <someData2>Some data 2</someData2>
  <someData3>Some data 3</someData3>
  <promotions>
    <promotion>
      <header>
        <id>3</id>
        <name>Promotion 3</name>
      </header>
      <positions>
        <position>
          <id>4</id>
          <name>Position 3a</name>
        </position>
      </positions>
    </promotion>
  </promotions>
</root>

要仅处理 Id=2 的促销,您可以使用(来自 root 的上下文):

<xsl:apply-templates select="promotions/promotion[header/id='2']"/>

已添加:

这是一个示例,展示了如何使用覆盖 XML 文档 "merge" 输入 XML。给定:

XML

<root>
  <info>
    <creationDate>2015-03-11 11:45:49</creationDate>
  </info>
  <promotions>
    <promotion>
      <header>
        <id>1</id>
        <name>Promotion 1</name>
      </header>
      <positions>
        <position>
          <id>1</id>
          <name>Position 1a</name>
        </position>
        <position>
          <id>2</id>
          <name>Position 1b</name>
        </position>
      </positions>
    </promotion>
    <promotion>
      <header>
        <id>2</id>
        <name>Promotion 2</name>
      </header>
      <positions>
        <position>
          <id>3</id>
          <name>Position 2a</name>
        </position>
      </positions>
    </promotion>
  </promotions>
</root>

new.xml

<newPromotions>
    <promotion>
      <header>
        <id>2</id>
        <name>New Promotion 2</name>
      </header>
      <positions>
        <position>
          <id>4</id>
          <name>New Position 2A</name>
        </position>
      </positions>
    </promotion>
    <promotion>
      <header>
        <id>3</id>
        <name>New Promotion 3</name>
      </header>
      <positions>
        <position>
          <id>5</id>
          <name>New Position 3A</name>
        </position>
      </positions>
    </promotion>
</newPromotions>

XSLT 1.0

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

<xsl:param name="new-path" select="'path/to/new.xml'"/>
<xsl:variable name="new-promotions" select="document($new-path)/newPromotions/promotion" />

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

<xsl:template match="promotions">
    <xsl:copy>
        <xsl:apply-templates select="promotion[not(header/id=$new-promotions/header/id)]"/>
        <xsl:apply-templates select="$new-promotions"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

结果

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <info>
      <creationDate>2015-03-11 11:45:49</creationDate>
   </info>
   <promotions>
      <promotion>
         <header>
            <id>1</id>
            <name>Promotion 1</name>
         </header>
         <positions>
            <position>
               <id>1</id>
               <name>Position 1a</name>
            </position>
            <position>
               <id>2</id>
               <name>Position 1b</name>
            </position>
         </positions>
      </promotion>
      <promotion>
         <header>
            <id>2</id>
            <name>New Promotion 2</name>
         </header>
         <positions>
            <position>
               <id>4</id>
               <name>New Position 2A</name>
            </position>
         </positions>
      </promotion>
      <promotion>
         <header>
            <id>3</id>
            <name>New Promotion 3</name>
         </header>
         <positions>
            <position>
               <id>5</id>
               <name>New Position 3A</name>
            </position>
         </positions>
      </promotion>
   </promotions>
</root>

回应您的编辑#4:

So in my case filtering would mean: "Take everything from root element, but filter promotions so only those which Ids are NOT in the external file, will be processed".

我上面的回答做了两件事:

  1. 它从输入 XML 文档中复制所有内容 除了 推广其 ID 在外部文件中;
  2. 添加 外部文件中列出的所有促销。

如果你只想做#1 而不想做#2,那么改变这个:

<xsl:template match="promotions">
    <xsl:copy>
        <xsl:apply-templates select="promotion[not(header/id=$new-promotions/header/id)]"/>
        <xsl:apply-templates select="$new-promotions"/>
    </xsl:copy>
</xsl:template>

至:

<xsl:template match="promotions">
    <xsl:copy>
        <xsl:apply-templates select="promotion[not(header/id=$new-promotions/header/id)]"/>
    </xsl:copy>
</xsl:template>

在给定的示例中,结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <info>
      <creationDate>2015-03-11 11:45:49</creationDate>
   </info>
   <promotions>
      <promotion>
         <header>
            <id>1</id>
            <name>Promotion 1</name>
         </header>
         <positions>
            <position>
               <id>1</id>
               <name>Position 1a</name>
            </position>
            <position>
               <id>2</id>
               <name>Position 1b</name>
            </position>
         </positions>
      </promotion>
   </promotions>
</root>