如何将XSLT模板应用于具有相同结构的多个不同元素?

时间:2015-04-27 22:34:43

标签: xml xslt xsd

我已经搜索过谷歌和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>
&#13;
&#13;
&#13;

例如,在上面的示例中,Model2Model6Model8是相同的结构,我需要对每个元素应用相同的转换。我想做这样的事情:

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

并简单地转换父元素。这可能吗?

我知道我可以这样做:

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

然后我重复了很多代码。有什么建议吗?

修改

我在原帖中过于笼统,抱歉。 一个更具体的例子(在我的例子中,是我拥有的对象之一)将是一个地址和与它们相关联的子类型。几种不同类型的元素具有地址。例如,我有一个Product元素,其中包含卖方,供应商,转售商,利益相关者列表,每个都有一个地址等。每个地址都有不同的名称。

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

现在这不是所有具有地址的元素,但是如果我采用我之前已经完成的方法,我将不得不制作一个模板列表(如我上面提到的)或者长时间使用像michael.hor257k建议的比赛列表。

&#13;
&#13;
<xsl:template match="Address | Business | SecondaryAddress | WorkAddress | Home | SecondaryAddress | BusinessAddress | ...">
  <!-- apply the same transform to all of these -->
</xsl:template>
&#13;
&#13;
&#13;

为了解决我的问题,每个地址由多个子类型组成:AddressCategory,District,Zone,Street,StreetType,PostDirection,PreDirection等。这些类型中的每一个都具有相同的结构,但现在我遇到了如何匹配每种类型的问题。一些子类型,如Street,在某些地方(在其他元素中使用时)和#34; CodeType&#34;在其他人。如果我做了一场比赛,我最终会看到一些非常丑陋的东西,我甚至还没有完成整个文件:

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

我是否坚持使用难以阅读,调试和维护xslt是更好的方法吗?

2 个答案:

答案 0 :(得分:1)

很难回答你的问题,因为它完全与上下文有关 - 并且你没有提供完整的上下文。

  

例如,在上面的示例中,Model2,Model6和Model8是   相同的结构,我需要对每个元素应用相同的转换

你为什么不这样做:

<xsl:template match="Model2 | Model6 |  Model8">
    <!-- apply the same transform to all of these -->
</xsl:template>

请注意,此模板可以从任何父节点应用 一个Model2和/或Model6和/或Model8(实际上,它也可以从其他上下文中应用,但这可能不是这里的重点)。

  

如何编写一个可以从内部调用的模板   我需要编写的50个模板中的每一个

您确定需要编写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>   

即便如此,我也不确定替代方案是否比最初的建议更具吸引力。

答案 1 :(得分:1)

如果您可以访问支持模式的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....">

并记住每次架构更改时都要更改它。