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".
我上面的回答做了两件事:
- 它从输入 XML 文档中复制所有内容 除了 推广其 ID 是 在外部文件中;
- 它添加 外部文件中列出的所有促销。
如果你只想做#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>
我有以下 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".
我上面的回答做了两件事:
- 它从输入 XML 文档中复制所有内容 除了 推广其 ID 是 在外部文件中;
- 它添加 外部文件中列出的所有促销。
如果你只想做#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>