如何将 XSLT 模板应用于具有相同结构的多个不同元素?
How can I apply a XSLT template to several different elements with the same structure?
我已经搜索了 Google 和 Stack Overflow,但一直找不到答案。如果这是重复的,我深表歉意。
我有一个 XML 文档,其中包含一些嵌套类型;但是,整个文档中出现了几个重复的元素,它们的名称和父元素都不同。对比XSD,发现这些重复的元素其实是同一类对象,只是通篇引用而已。大约有 50 种不同的类型引用了这个重复元素。我如何编写一个模板,然后可以从我需要编写的 50 个模板中的每一个模板中调用,这样我就不必为每个父类型复制转换?
例如:
<Model1>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model2>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
</Model2>
</Model1>
<Model3>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model4>
<Model5>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model6>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
</Model6>
</Model5>
<Code>code1</Code>
<type>VMO</type>
</Model4>
</Model3>
<Model7>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model8>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
</Model8>
</Model7>
例如,在上面的示例中,Model2
、Model6
和 Model8
是相同的结构,我需要对每个元素应用相同的转换。我想做这样的事情:
<xsl:template match="Model1">
<xsl:copy>
<!-- maps all other elements -->
<xsl:apply-templates select="someSpecialFunction(Model3)" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model5">
<xsl:copy>
<!-- maps all other elements -->
<xsl:apply-templates select="someSpecialFunction(Model6)" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model7">
<xsl:copy>
<!-- maps all other elements -->
<xsl:apply-templates select="someSpecialFunction(Model8)" />
</xsl:copy>
</xsl:template>
并简单地转换父元素。这可能吗?
我知道我可以做这样的事情:
<xsl:template match="Model1/Model3">
<xsl:copy>
<!-- Transfrom type -->
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model5/Model6">
<xsl:copy>
<!-- Transfrom type -->
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model7/Model8">
<xsl:copy>
<!-- Transfrom type -->
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
但后来我重复了很多代码。有什么建议吗?
编辑:
我原来的post太笼统了,对此感到抱歉。
一个更具体的例子(在我的例子中,是我拥有的对象之一)是一个地址和与之关联的子类型。几种不同类型的元素都有一个地址。例如,我有一个 Product 元素,其中包含 Seller、Supplier、Reseller、利益相关者列表,每个利益相关者都有一个地址等。这些地址中的每一个都有不同的名称。
<Product>
<Seller>
<BusinessAddress>...</BusinessAddress>
<DistributionAddress>...</DistributionAddress>
<MainContactAddress>...</MainContactAddress>
</Seller>
<ReSeller>
<BusinessAddress>...</BusinessAddress>
<StoreManagerAddress>...</StoreManagerAddress>
<MainContactAddress>...</MainContactAddress>
</ReSeller>
<Supplier>
<BusinessAddress>...</BusinessAddress>
<DistributionCenterAddress>...</DistributionCenterAddress>
<MainContactAddress>...</MainContactAddress>
</Supplier>
<Stakeholders>
<Entity>
<HomeAddress>...</HomeAddress>
<WorkAddress>...</WorkAddress>
<SecondaryAddress>...</SecondaryAddress>
<Street>...</Street>
<CrossStreet>...</CrossStreet>
</Entity>
</Stakeholders>
</Product>
<SurveyRespondents>
<SurveyRespondent>
<Address>...</Address>
<Business>...</Business>
</SurveyRespondent>
<SurveyRespondent>
<Address>...</Address>
<Business>...</Business>
</SurveyRespondent>
</SurveyRespondents>
现在这不是所有具有地址的元素,但是如果我采用我之前做过的方法,我将不得不制作一个模板列表(就像我上面提到的那样)或者使用很长的建议的 michael.hor257k 等匹配列表。
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
<!-- apply the same transform to all of these -->
</xsl:template>
为了解决我的问题,每个地址都由多个子类型组成:AddressCategory、District、Zone、Street、StreetType、PostDirection、PreDirection 等。这些类型中的每一个都具有相同的结构,但现在我 运行 进入我如何匹配每种类型的问题。一些子类型,如 Street
,在某些地方是一个字符串(在其他元素中使用时),在其他地方是 "CodeType"。如果我进行匹配,我会得到一些非常丑陋的东西,我什至还没有完成整个文档:
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
<!-- apply the same transform to all of these -->
</xsl:template>
<xsl:template match="Address/Street | Business/Street | SecondaryAddress/Street | WorkAddress/Street | Home/Street | SecondaryAddress/Street | BusinessAddress/Street | AddressType | District | Zone...">
<!-- apply the same transform to all of these -->
</xsl:template>
我是否坚持难以阅读、调试和维护 xslt 是更好的方法吗?
很难回答您的问题,因为它全都与上下文有关 - 而您没有提供完整的上下文。
In the example above for instance, Model2, Model6 and Model8 are the
same structure and I need to apply the same transform to each element
你为什么不这样做:
<xsl:template match="Model2 | Model6 | Model8">
<!-- apply the same transform to all of these -->
</xsl:template>
请注意,此模板可以从作为父节点的任何节点应用
a Model2 and/or Model6 and/or Model8(实际上,它也可以从其他上下文中应用,但这可能不是这里的重点)。
How can I write one template that can then be called from inside of
each of the 50 templates I need to write
你确定要写50个模板吗?
重新编辑:
替代方案:
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
<!-- apply the same transform to all of these -->
</xsl:template>
将是:
<xsl:template name="address">
<xsl:param name="address-node" />
<!-- apply the transform -->
</xsl:template>
但是您必须从存在地址的每个位置显式调用此模板,例如:
<xsl:template match="Seller">
<xsl:copy>
<!-- other code -->
<xsl:call-template name="address">
<xsl:with-param name="address-node" select="BusinessAddress"/>
</xsl:call-template>
<xsl:call-template name="address">
<xsl:with-param name="address-node" select="DistributionAddress"/>
</xsl:call-template>
<xsl:call-template name="address">
<xsl:with-param name="address-node" select="MainContactAddress"/>
</xsl:call-template>
<!-- more code -->
</xsl:copy>
</xsl:template>
可能,这可以缩短为:
<xsl:template match="Seller">
<xsl:copy>
<!-- other code -->
<xsl:call-template name="address">
<xsl:with-param name="address-nodes" select="BusinessAddress | DistributionAddress | MainContactAddress"/>
</xsl:call-template>
<!-- more code -->
</xsl:copy>
</xsl:template>
即便如此,我不确定替代方案是否比最初的建议更有吸引力。
如果您可以访问模式感知 XSLT 2.0 处理器,这将变得非常简单。您可以定义一个模板规则来匹配具有给定类型 T
的所有元素,如下所示:
<xsl:template match="element(*, T)">
或者如果元素都在模式中定义为属于一个替换组,其头部是元素H
,您可以写
<xsl:template match="schema-element(H)">
没有架构感知处理器,您必须定义联合模式
<xsl:template match="A|B|C|D|E|F....">
并记得在每次架构更改时更改它。
我已经搜索了 Google 和 Stack Overflow,但一直找不到答案。如果这是重复的,我深表歉意。
我有一个 XML 文档,其中包含一些嵌套类型;但是,整个文档中出现了几个重复的元素,它们的名称和父元素都不同。对比XSD,发现这些重复的元素其实是同一类对象,只是通篇引用而已。大约有 50 种不同的类型引用了这个重复元素。我如何编写一个模板,然后可以从我需要编写的 50 个模板中的每一个模板中调用,这样我就不必为每个父类型复制转换?
例如:
<Model1>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model2>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
</Model2>
</Model1>
<Model3>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model4>
<Model5>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model6>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
</Model6>
</Model5>
<Code>code1</Code>
<type>VMO</type>
</Model4>
</Model3>
<Model7>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
<Model8>
<Description>desc1</Description>
<Code>code1</Code>
<type>VMO</type>
</Model8>
</Model7>
例如,在上面的示例中,Model2
、Model6
和 Model8
是相同的结构,我需要对每个元素应用相同的转换。我想做这样的事情:
<xsl:template match="Model1">
<xsl:copy>
<!-- maps all other elements -->
<xsl:apply-templates select="someSpecialFunction(Model3)" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model5">
<xsl:copy>
<!-- maps all other elements -->
<xsl:apply-templates select="someSpecialFunction(Model6)" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model7">
<xsl:copy>
<!-- maps all other elements -->
<xsl:apply-templates select="someSpecialFunction(Model8)" />
</xsl:copy>
</xsl:template>
并简单地转换父元素。这可能吗?
我知道我可以做这样的事情:
<xsl:template match="Model1/Model3">
<xsl:copy>
<!-- Transfrom type -->
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model5/Model6">
<xsl:copy>
<!-- Transfrom type -->
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Model7/Model8">
<xsl:copy>
<!-- Transfrom type -->
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
但后来我重复了很多代码。有什么建议吗?
编辑:
我原来的post太笼统了,对此感到抱歉。 一个更具体的例子(在我的例子中,是我拥有的对象之一)是一个地址和与之关联的子类型。几种不同类型的元素都有一个地址。例如,我有一个 Product 元素,其中包含 Seller、Supplier、Reseller、利益相关者列表,每个利益相关者都有一个地址等。这些地址中的每一个都有不同的名称。
<Product>
<Seller>
<BusinessAddress>...</BusinessAddress>
<DistributionAddress>...</DistributionAddress>
<MainContactAddress>...</MainContactAddress>
</Seller>
<ReSeller>
<BusinessAddress>...</BusinessAddress>
<StoreManagerAddress>...</StoreManagerAddress>
<MainContactAddress>...</MainContactAddress>
</ReSeller>
<Supplier>
<BusinessAddress>...</BusinessAddress>
<DistributionCenterAddress>...</DistributionCenterAddress>
<MainContactAddress>...</MainContactAddress>
</Supplier>
<Stakeholders>
<Entity>
<HomeAddress>...</HomeAddress>
<WorkAddress>...</WorkAddress>
<SecondaryAddress>...</SecondaryAddress>
<Street>...</Street>
<CrossStreet>...</CrossStreet>
</Entity>
</Stakeholders>
</Product>
<SurveyRespondents>
<SurveyRespondent>
<Address>...</Address>
<Business>...</Business>
</SurveyRespondent>
<SurveyRespondent>
<Address>...</Address>
<Business>...</Business>
</SurveyRespondent>
</SurveyRespondents>
现在这不是所有具有地址的元素,但是如果我采用我之前做过的方法,我将不得不制作一个模板列表(就像我上面提到的那样)或者使用很长的建议的 michael.hor257k 等匹配列表。
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
<!-- apply the same transform to all of these -->
</xsl:template>
为了解决我的问题,每个地址都由多个子类型组成:AddressCategory、District、Zone、Street、StreetType、PostDirection、PreDirection 等。这些类型中的每一个都具有相同的结构,但现在我 运行 进入我如何匹配每种类型的问题。一些子类型,如 Street
,在某些地方是一个字符串(在其他元素中使用时),在其他地方是 "CodeType"。如果我进行匹配,我会得到一些非常丑陋的东西,我什至还没有完成整个文档:
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
<!-- apply the same transform to all of these -->
</xsl:template>
<xsl:template match="Address/Street | Business/Street | SecondaryAddress/Street | WorkAddress/Street | Home/Street | SecondaryAddress/Street | BusinessAddress/Street | AddressType | District | Zone...">
<!-- apply the same transform to all of these -->
</xsl:template>
我是否坚持难以阅读、调试和维护 xslt 是更好的方法吗?
很难回答您的问题,因为它全都与上下文有关 - 而您没有提供完整的上下文。
In the example above for instance, Model2, Model6 and Model8 are the same structure and I need to apply the same transform to each element
你为什么不这样做:
<xsl:template match="Model2 | Model6 | Model8">
<!-- apply the same transform to all of these -->
</xsl:template>
请注意,此模板可以从作为父节点的任何节点应用 a Model2 and/or Model6 and/or Model8(实际上,它也可以从其他上下文中应用,但这可能不是这里的重点)。
How can I write one template that can then be called from inside of each of the 50 templates I need to write
你确定要写50个模板吗?
重新编辑:
替代方案:
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
<!-- apply the same transform to all of these -->
</xsl:template>
将是:
<xsl:template name="address">
<xsl:param name="address-node" />
<!-- apply the transform -->
</xsl:template>
但是您必须从存在地址的每个位置显式调用此模板,例如:
<xsl:template match="Seller">
<xsl:copy>
<!-- other code -->
<xsl:call-template name="address">
<xsl:with-param name="address-node" select="BusinessAddress"/>
</xsl:call-template>
<xsl:call-template name="address">
<xsl:with-param name="address-node" select="DistributionAddress"/>
</xsl:call-template>
<xsl:call-template name="address">
<xsl:with-param name="address-node" select="MainContactAddress"/>
</xsl:call-template>
<!-- more code -->
</xsl:copy>
</xsl:template>
可能,这可以缩短为:
<xsl:template match="Seller">
<xsl:copy>
<!-- other code -->
<xsl:call-template name="address">
<xsl:with-param name="address-nodes" select="BusinessAddress | DistributionAddress | MainContactAddress"/>
</xsl:call-template>
<!-- more code -->
</xsl:copy>
</xsl:template>
即便如此,我不确定替代方案是否比最初的建议更有吸引力。
如果您可以访问模式感知 XSLT 2.0 处理器,这将变得非常简单。您可以定义一个模板规则来匹配具有给定类型 T
的所有元素,如下所示:
<xsl:template match="element(*, T)">
或者如果元素都在模式中定义为属于一个替换组,其头部是元素H
,您可以写
<xsl:template match="schema-element(H)">
没有架构感知处理器,您必须定义联合模式
<xsl:template match="A|B|C|D|E|F....">
并记得在每次架构更改时更改它。