XSLT - 棘手的转换 [英] XSLT - Tricky Transformation

查看:20
本文介绍了XSLT - 棘手的转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在处理 XML 片段的 XSLT 转换时遇到问题.源 XML 如下所示:

I'm having trouble with an XSLT transformation of XML fragments. The source XML looks like so:

<XXX>
    <Name>Sample</Name>
    <MMM>
        <AAA ID="A"/>
        <MMM>
            <BBB ID="B"/>
            <MMM>
                <AA ID="C"/>
                <BB ID="D"/>
            </MMM>
        </MMM>
    </MMM>
</XXX>

但需要转化为:

<XXX>
    <Name>Sample</Name>
    <MMM>
        <MMM>
            <MMM>
                <AAA ID="A"/>
                <BBB ID="B"/>
            </MMM>
            <AA ID="C"/>            
        </MMM>
        <BB ID="D"/>
    </MMM>
</XXX>

规则很简单,MMM元素只能有两个子元素节点.如果这些节点中只有一个恰好是另一个 MMM,则它需要占据第一个位置.

The rule is simple, the MMM element can only have two child element nodes. If only one of those nodes happen to be another MMM, it needs to occupy the first position.

使用代码很容易,但这些 XML 片段是 SQL 数据库中 XML 列的值,我想使用 SQL 和 XSLT 来更新这些值.

It is easy using code, but these XML fragments are values to XML columns in an SQL database, and I want to use SQL along with XSLT to update those values.

有任何指示或建议吗?

推荐答案

解决这个问题的一种方法是首先提取一方面的MMM树和other另一方面,节点变成单独的结构,然后再次合并它们.

One way to solve this is to first extract the MMM tree on the one hand and the other nodes on the other hand into separate structures and then to merge them again.

这真是一个不错的挑战!让我熬夜到凌晨 4:00.

This was really a nice challenge! Made me stay up until 4:00 AM.

以下 XSLT(几乎!见下文)可以完成这项工作:

The following XSLT (almost! see below) does the job:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" exclude-result-prefixes="exslt">
   <xsl:output method="xml" encoding="ISO-8859-1" />
   <!-- handling of MMM extraction -->
   <xsl:template match="MMM" mode="extract_mmm">
      <MMM>
         <xsl:apply-templates mode="extract_mmm" />
      </MMM>
   </xsl:template>
   <xsl:template match="*" mode="extract_mmm" />
   <!-- handling of extraction of other nodes -->
   <xsl:template match="MMM" mode="extract_other">
      <xsl:apply-templates mode="extract_other" />
   </xsl:template>
   <xsl:template match="*" mode="extract_other">
      <xsl:copy-of select="." />
   </xsl:template>
   <!-- handling of merging the two partial result sets -->
   <xsl:template match="MMM" mode="dump">
      <xsl:param name="others" />
      <xsl:choose>
         <!-- this handles the case of an MMM being a leaf node -->
         <xsl:when test="count(MMM) = 0">
            <xsl:variable name="nodes_in_next_sibling" select="2*count(following-sibling::MMM)" />
            <MMM>
               <xsl:copy-of select="$others[count($others) - $nodes_in_next_sibling - 1]" />
               <xsl:copy-of select="$others[count($others) - $nodes_in_next_sibling]" />
            </MMM>
         </xsl:when>
         <!-- this handles the case of an inner MMM with a sibling -->
         <xsl:when test="count(../MMM) = 2">
            <xsl:variable name="free_positions_in_second_child" select="count(MMM[position() = 2 and count(MMM) = 0])*2 + count(MMM[2]//MMM[count(MMM) = 0])*2 + count(MMM[position() = 2 and count(MMM) = 1]) + count(MMM[2]//MMM[count(MMM) = 1])" />
            <MMM>
               <xsl:apply-templates mode="dump" select="MMM[1]">
                  <xsl:with-param name="others" select="$others[position() &lt; count($others)- $free_positions_in_second_child + 1]" />
               </xsl:apply-templates>
               <xsl:apply-templates mode="dump" select="MMM[2]">
                  <xsl:with-param name="others" select="$others[position() &gt;= count($others) - $free_positions_in_second_child + 1]" />
               </xsl:apply-templates>
            </MMM>
         </xsl:when>
         <!-- this handles the case of an inner MMM without sibling -->
         <xsl:when test="count(../MMM) = 1">
            <MMM>
               <xsl:apply-templates mode="dump">
                  <xsl:with-param name="others" select="$others[position() &lt; count($others)]" />
               </xsl:apply-templates>
            </MMM>
            <xsl:copy-of select="$others[position() = count($others)]" />
         </xsl:when>
      </xsl:choose>
   </xsl:template>
   <xsl:template match="XXX">
      <XXX>
         <xsl:copy-of select="Name" />
         <xsl:variable name="mmm_structure">
            <xsl:apply-templates mode="extract_mmm" select="MMM" />
         </xsl:variable>
         <xsl:variable name="other_structure_tmp">
            <xsl:apply-templates mode="extract_other" select="MMM" />
         </xsl:variable>
         <!-- http://stackoverflow.com/questions/4610921/how-to-concatenate-two-node-sets-such-that-order-is-respected -->
         <!-- http://www.exslt.org/exsl/ -->
         <xsl:variable name="other_structure" select="exslt:node-set($other_structure_tmp/*)" />
         <xsl:apply-templates select="$mmm_structure" mode="dump">
            <xsl:with-param name="others" select="$other_structure" />
         </xsl:apply-templates>
      </XXX>
   </xsl:template>
</xsl:stylesheet>

注意事项:

  • 这几乎仍然是带有 EXSLT 扩展的 XSLT 1.0.
  • MMM 具有两个子节点的节点也被处理.other 节点的放置规则是从下到上填充位置.
  • 在此版本中,其他 节点在不违反 2 个子节点最大规则的情况下尽可能向下移动.所以输出并不完全像问题中显示的那样,而是如下所示.
  • This is still pretty much XSLT 1.0 with EXSLT extensions.
  • MMM nodes with two children are also handled. The rule of placing the other nodes is such that the positions are filled from the bottom to the top.
  • In this version the other nodes are moved down as far as possible without violating the 2 children max rule. So the output is not exactly as display in the question but rather as follows.

这是输出:

<?xml version="1.0" encoding="ISO-8859-1"?>
<XXX>
  <Name>Sample</Name>
  <MMM>
    <MMM>
      <MMM>
        <AAA ID="A"/>
        <AAA ID="B"/>
      </MMM>
    </MMM>
    <BBB ID="C"/>
  </MMM>
  <BBB ID="D"/>
</XXX>

如果这是一个问题,请告诉我.

If this is a problem let me know.

这篇关于XSLT - 棘手的转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆