如何防止值被覆盖?

时间:2012-09-20 22:12:17

标签: xslt transform apply-templates

我无法转换XML消息的某些元素。我需要交换一些值,但在我将最后一个节点的值复制到第一个节点之后,我无法达到第一个节点的原始值。

我一直在寻找几个小时,我在XSL中尝试了几种变体,但没有一个给出令人满意的结果。我不是一个非常有经验的XSL程序员,但我相信解决方案不会那么困难。我简化了原始信息以解释问题,我希望你们中的一个可以帮助我解决这个问题。让我通过以下例子解释:

来源:

<ProcessMessages>
  <Message>
    <SomeNode>Val</SomeNode>
    <SomeNode>Val</SomeNode>
    <Number>100000</Number>
    <ExternalRefID>EXX12345600000001</ExternalRefID>
    <SomeNode>Val</SomeNode>
    <SomeNode>Val</SomeNode>
  </Message>
  <RelatedMessages>
    <Message>     
      <SomeNode>Val</SomeNode>
      <Number>200000</Number>
      <ExternalRefID>EXX12345600000002</ExternalRefID>
      <SomeNode>Val</SomeNode>
    </Message>
    <Message>
      <SomeNode>Val</SomeNode>
      <Number>300000</Number>
      <ExternalRefID>EXX12345600000003</ExternalRefID>
      <SomeNode>Val</SomeNode>
    </Message>
  </RelatedMessages>
</ProcessMessages>

期望的转型:

<ProcessMessages>
  <Message>
    <SomeNode>Val</SomeNode>
    <SomeNode>Val</SomeNode>
    <ExternalRefID>EXX12345600000003</ExternalRefID>
    <SomeNode>Val</SomeNode>
    <SomeNode>Val</SomeNode>
  </Message>
  <RelatedMessages>
    <Message>     
      <Number>200000</Number>
      <ExternalRefID>EXX12345600000002</ExternalRefID>
    </Message>
    <Message>
      <Number>100000</Number>
      <ExternalRefID>EXX12345600000001</ExternalRefID>
    </Message>
  </RelatedMessages>
</ProcessMessages>

我的XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

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

<xsl:template match="//ProcessMessages/Message/ExternalRefID">
    <xsl:apply-templates select="//ProcessMessages/RelatedMessages/Message[last()]/ExternalRefID"/>
  </xsl:template>
  <xsl:template match="//ProcessMessages/Message/Number">
    <xsl:apply-templates select="//ProcessMessages/RelatedMessages/Message[last()]/Number"/>
  </xsl:template>

  <xsl:template match="//ProcessMessages/RelatedMessages/Message[1]">
    <xsl:element name="Message">
      <xsl:apply-templates select="ExternalRefID"/>
      <xsl:apply-templates select="Number"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="//ProcessMessages/RelatedMessages/Message[2]">
    <xsl:element name="Message">
      <xsl:apply-templates select="//ProcessMessages/Message/ExternalRefID"/>
      <xsl:apply-templates select="//ProcessMessages/Message/Number"/>
    </xsl:element>
  </xsl:template>  
</xsl:stylesheet>

当前结果

<ProcessMessages>
  <Message>
    <SomeNode>Val</SomeNode>
    <SomeNode>Val</SomeNode>
    <Number>100000</Number>
    <ExternalRefID>EXX12345600000001</ExternalRefID>
    <SomeNode>Val</SomeNode>
    <SomeNode>Val</SomeNode>
  </Message>
  <RelatedMessages>
    <Message>
      <ExternalRefID>EXX12345600000002</ExternalRefID>
      <Number>200000</Number>
    </Message>
    <Message>
      <ExternalRefID>EXX12345600000001</ExternalRefID>
      <Number>100000</Number>
    </Message>
  </RelatedMessages>
</ProcessMessages>

请注意,将ProcessMessages/RelatedMessages/Message的值复制到ProcessMessages/Message后,我无法达到默认值ProcessMessages/Message,因为它似乎被覆盖了。

有什么想法吗?如果有人可以帮助我,我将非常感激!

1 个答案:

答案 0 :(得分:0)

不,你没有覆盖输入中的任何内容。 XSLT转换的输出树是输入的单独对象,输入不可变。输入中的任何内容都不会被覆盖或更改。

是的,你是对的:你的问题有一个简单的解决方案。

问题是您尝试将ExternalRefID和Number of / ProcessMessages / Message复制到RelatedMessages元素的末尾采用以下形式:

<xsl:apply-templates 
  select="//ProcessMessages
           /Message/ExternalRefID"/>
<xsl:apply-templates 
  select="//ProcessMessages
           /Message/Number"/>

样式表引擎按您的要求执行,并为这些元素寻找模板。它在示例代码中找到第二个和第三个模板,这些模板从相关消息中的最后一个Message元素中查找和复制值。

我并不完全清楚如何定义您正在尝试构建的转换,但 if 您要尝试做的是(1)将RelatedMessages的最终消息移动到ProcessMessages的第一个子节点,以及(2)将ProcessMessages的第一个子节点移动到RelatedMessages的末尾,那么这样的东西就可以工作:

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

<xsl:template match="ProcessMessages">
  <xsl:copy-of 
    select="RelatedMessages/Message[last()]"/>
  <xsl:apply-templates select="RelatedMessages"/>
</xsl:template>

<xsl:template 
  match="RelatedMessages/Message[last()]">
  <xsl:copy-of 
    select="../../self::ProcessMessages
            /Message[1]"/>
</xsl:template>  

如果您只想 更改Number和ExternalRefID,那么您需要适当修改此代码。