【问题标题】:Merging/updating XML using XSLT 1.0 with certain conditions在特定条件下使用 XSLT 1.0 合并/更新 XML
【发布时间】:2014-01-09 16:57:24
【问题描述】:

美好的一天,所以我有这个传递给 xslt 的示例 xml 文件:(我将使用的实际 xml 更长)

<RecordMerge>
   <ExistingRecord>      
      <Record>
         <RecordNumber>990000010</RecordNumber>
         <Name>Michel Duchovskiy</Name>
         <Gender />
         <Address>
            <Street>555 Ocean Blvd</Street>
            <City>Atlantic City</City>
            <Zip>23345</Zip>
         </Address>
      </Record>
   </ExistingRecord>

   <NewRecord>
      <Record>
         <RecordNumber>990000010</RecordNumber>
         <Name>Mick Dick</Name>
         <Gender>Male</Gender>
         <MaritalStatus>Married</MaritalStatus>
         <Address>
            <Street>123 Sunset Blvd</Street>
            <City>Beverly Hills</City>
            <Zip>90210</Zip>
         </Address>
      </Record>
   </NewRecord>

</RecordMerge>

所以任务是,ExistingRecord/Record 内容需要用 NewRecord/Record 内容更新。忘了说它不需要根据RecordNumber进行匹配。匹配是预先完成的,传递给此 xslt 的 XML 将始终具有相同的结构。 RecordMerge 根总是有两个孩子,所以永远不需要匹配。

合并条件:

-当元素没有子元素(如性别、姓名)时:

  • 如果元素在 Existing 中为空白 - 更新值
  • 如果元素是新元素且不在 Existing 中,则添加到结果中

-当元素有子元素时(比如我们,如果它是像地址这样的列表):

  • 更新内容

否则,ExistingRecord/Record 中的元素应该保持不变。

结果应该是这样的:

    <Record>
         <Name>Michel Duchovskiy</Name>
         <Gender>Male</Gender>
         <MaritalStatus>Married</MaritalStatus>
         <Address>
            <Street>123 Sunset Blvd</Street>
            <City>Beverly Hills</City>
            <Zip>90210</Zip>
         </Address>
      </Record>

如您所见,名称保持不变,添加了性别值(因为它是空的)并且地址已更新。这只是一个示例,因为 xml 包含数百个不同的列表和元素。

不知道怎么做,因为它比简单的合并要复杂得多。谢谢。

【问题讨论】:

    标签: xml xslt merge conditional


    【解决方案1】:

    首先,我将从 XSLT 标识模板开始处理您不需要更改的元素

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

    这意味着要更改 ExistingRecord,您将拥有一个模板来匹配其 Record 元素的字段

    <xsl:template match="ExistingRecord/Record/*">
    

    在此您可以声明一个变量来引用相应的 NewRecord 字段,假设 RecordMerge

    中只有一个变量
    <xsl:variable name="new" 
         select="../../../NewRecord/Record/*[local-name() = local-name(current())]" />
    

    那么您可以使用 xsl:choose 语句来检查是否使用 NewRecord 或 ExistingRecord 字段使用您的逻辑声明

    <xsl:choose>
       <xsl:when test="$new and *"><xsl:apply-templates select="$new" /></xsl:when>    
       <xsl:when test="$new and not(normalize-space())"><xsl:apply-templates select="$new" /></xsl:when>
       <xsl:otherwise><xsl:call-template name="identity" /></xsl:otherwise>
    </xsl:choose>
    

    所以,第一个条件检查现有元素是否有子元素,第二个条件检查现有元素是否为空。

    这只会将节点添加到当前不存在的ExistingRecord。为此,您可以定义一个键来查找 RecordMerge id 和元素名称存在的子元素(我假设您的 XML 中可以有多个 RecordMerge 元素)

    <xsl:key name="existing" 
             match="ExistingRecord/Record/*" 
             use="concat(generate-id(../../..), '|', local-name())" />
    

    然后,要将元素从 NewRecord 复制到 ExistingRecord 中,而这些元素在 ExistingRecord 中不存在,该语句看起来会有所不同像这样

      <xsl:apply-templates select="../../NewRecord/Record/*[not(key('existing', concat($RecordMerge, '|', local-name())))]" />
    

    (其中 $RecordMerge 包含当前 RecordMerge 元素的 generate-id)

    试试这个 XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output omit-xml-declaration="yes" indent="yes" />
    
      <xsl:key name="existing" match="ExistingRecord/Record/*" use="concat(generate-id(../../..), '|', local-name())" />
    
      <xsl:template match="ExistingRecord">
         <xsl:apply-templates select="Record" />
      </xsl:template>
    
      <xsl:template match="NewRecord" />
    
      <xsl:template match="ExistingRecord/Record">
        <xsl:copy>
          <xsl:apply-templates />
          <xsl:variable name="RecordMerge" select="generate-id(../..)" />
          <xsl:apply-templates select="../../NewRecord/Record/*[not(key('existing', concat($RecordMerge, '|', local-name())))]" />
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="ExistingRecord/Record/*">
        <xsl:variable name="new" select="../../../NewRecord/Record/*[local-name() = local-name(current())]" />
        <xsl:choose>
           <xsl:when test="$new and *"><xsl:apply-templates select="$new" /></xsl:when>    
           <xsl:when test="$new and not(normalize-space())"><xsl:apply-templates select="$new" /></xsl:when>
           <xsl:otherwise><xsl:call-template name="identity" /></xsl:otherwise>
        </xsl:choose>
      </xsl:template>
    
      <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
    

    值得说明的是,此解决方案将在现有子元素之后添加新的子元素,而不是像您在预期输出中显示的那样在中间添加。 (因为我不知道该怎么做.....)

    【讨论】:

    • 感谢您的详细解释,非常感谢。我试过了,效果很好!
    • 但是我忘了说它不需要根据RecordNumber来匹配。匹配是预先完成的,传递给此 xslt 的 XML 将始终具有相同的结构。 RecordMerge 根总是有两个孩子,所以永远不需要匹配。我会尽量简化它,但如果你能告诉我没有 RecordNumber 匹配的解决方案,我将永远感激不尽!
    • 我已更新我的答案,使其不再与 RecordNumber 匹配。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多