【问题标题】:Recursion and TreeNode xslt递归和 TreeNode xslt
【发布时间】:2016-08-08 10:08:02
【问题描述】:

我刚开始接触 xslt,我正在做一些小任务来熟悉 xslt。但我遇到了一个无法解决的问题,无法将 xml 文件转换为另一个文件。 场景: 输入是一个包含节点的xml文件,每一个都是一对父子标签,标签用标签表示当前节点的名字,用标签表示父节点的名字。 我想要做的是生成一个节点树,第一个节点是没有父亲的节点(我手动创建了它,名称为 0 和树 1 中的级别),然后查找所有具有父亲标记的节点等于到(0)作为第一步,我将进入我的输入文件(1和4),在这里我在节点(0)中创建一个新节点,其中包含名称(1)并且在树中的级别等于(2 ) 然后我会去寻找所有父亲标签等于 (1) 的节点,依此类推,当我到达 (1) 没有更多子节点的地步时,我将创建另一个名称为 (4) 的节点并且在树中与名称为(1)的节点具有相同的级别,然后继续寻找父亲标签等于(4)的节点,依此类推。 我有这个 xml:

<TypedPolling xmlns="http://schemas.microsoft.com/Sql/2008/05/TypedPolling">
    <TypedPolling0>
        <TypedPolling0>
            <son>1</son>
            <father>0</father>
        </TypedPolling0>
        <TypedPolling0>
            <son>2</son>
            <father>1</father>
        </TypedPolling0>
        <TypedPolling0>
            <son>3</son>
            <father>0</father>
        </TypedPolling0>
        <TypedPolling0>
            <son>4</son>
            <father>3</father>
        </TypedPolling0>
    </TypedPolling0>
</TypedPolling>

之前的xml文件应该转换成这个xml文件:

<ns0:TreeNode>
    <ns0:node>0</ns0:node>
    <ns0:LevelInTree>1</ns0:LevelInTree>
    <ns0:TreeNode>
      <ns0:node>1</ns0:node>
      <ns0:LevelInTree>2</ns0:LevelInTree>
      <ns0:TreeNode>
        <ns0:node>2</ns0:node>
        <ns0:LevelInTree>3</ns0:LevelInTree>
      </ns0:TreeNode>
    </ns0:TreeNode>
    <ns0:TreeNode>
        <ns0:node>3</ns0:node>
        <ns0:LevelInTree>2</ns0:LevelInTree>
        <TreeNode>
            <ns0:node>4</ns0:node>
            <ns0:LevelInTree>3</ns0:LevelInTree>
        </TreeNode>
    </TreeNode>
</TreeNode>

我为转换输入而编写的代码是(我使用 key 元素来获取具有指定父亲姓名的儿子):

<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:msxsl="urn:schemas-microsoft-com:xslt"
            xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
            xmlns:s0="http://schemas.microsoft.com/Sql/2008/05/TypedPolling"
            xmlns:ns0="http://NO.Aditro.Schemas.Organization.OrganizationTrees"
            xmlns:userCSharp="http://schemas.microsoft.com/BizTalk/2003/userCSharp"
            exclude-result-prefixes="msxsl var s0 userCSharp">
<xsl:output omit-xml-declaration="yes" method="xml" indent="yes" version="1.0" />
<xsl:key name="KeyItemFather" match="/s0:TypedPolling/s0:TypedPolling0/s0:TypedPolling0" use="@father" />

<xsl:template match="/">
    <xsl:variable name="son" select="0" />
    <xsl:variable name="level" select="1" />
    <xsl:apply-templates mode="NewNode" select="/s0:TypedPolling/s0:TypedPolling0">
      <xsl:with-param name="level" select="$level" />
    </xsl:apply-templates>
</xsl:template>

<xsl:template name="NewTreeNode" match="/s0:TypedPolling/s0:TypedPolling0/s0:TypedPolling0" mode="NewNode">
<xsl:param name="son" />
<xsl:param name="level" />
    <ns0:TreeNode>
      <ns0:node>
        <xsl:value-of select="$son" />
      </ns0:node>
      <ns0:LevelInTree>
        <xsl:value-of select="$level" />
      </ns0:LevelInTree>
      <xsl:apply-templates select="key('KeyItemFather', $son)" mode="NewNode">
        <xsl:with-param name="level" select="$level+1" />
      </xsl:apply-templates>
    </ns0:TreeNode>
</xsl:template>
</xsl:stylesheet>

我遇到的问题是在递归过程中发生了一些错误,即递归不断进行并且&lt;ns0:node&gt;&lt;/ns0:node&gt; 保持为空(例如应该是&lt;ns0:node&gt;3&lt;/ns0:node&gt;)。我找不到我的错误在哪里! :(

【问题讨论】:

  • 欢迎来到 SO!请edit您的问题并添加有关您遇到的确切问题的信息 - 您是否收到错误消息?如果是,什么错误信息?您得到的输出是否与预期输出不同?如果是,怎么做?
  • 请解释所需转换的逻辑。 -- 另请注意,您向我们展示的输出有一个未定义的前缀。
  • @michael.hor257k 根据我从你的问题中了解到,我所做的转换就是要转换一个xml文件,它的节点包含 到节点树,每个节点都有一个级别,表示它与第一个节点的距离。如果我的问题不正确,请纠正我!
  • 重要的是我正确地理解了你的问题。在这一点上,我不知道你想做什么。请编辑您的问题并解释如果我手动执行此操作,我将如何准确地到达显示的输出。

标签: xml xslt xslt-1.0


【解决方案1】:

在我看来,您似乎非常接近:设计方法当然是正确的。您未向我们展示的内容可能存在错误,例如命名空间声明 - 展示完整的可运行样式表很有用,因此我们可以自己尝试并使用我们最喜欢的调试工具,而无需填写您遗漏的部分.

令我感到困惑的是,您的源数据以一个 TypedPolling 开始标签开头,后跟三个 TypedPolling0 开始标签。这些开始标签中的前两个没有相应的结束标签。假设您刚刚忽略了这些,您的 father 元素有四层深。所以你的xsl:key 声明在两个方面是错误的:首先它匹配father 属性而不是元素(use="@father"),其次它的祖先级别太少。请注意,您实际上不需要在此处指定完整路径,这样就足够了

<xsl:key name="KeyItemFather" match="s0:TypedPolling0" use="father" />

我注意到的另一件事是您的模板规则有两个参数,但(在两个调用中)您只提供一个。实际上,您不需要$son 参数,因为您可以通过从上下文节点访问child::son 来获取该信息。

【讨论】:

  • 尊敬的Kay先生,我已经添加了nameSpace并编辑了源代码,希望它变得更容易测试......谢谢
  • OK,这意味着xsl:key/@match现在是正确的,但是@use仍然是错误的,并且仍然存在您使用两个参数声明模板并且只使用一个调用它的问题。
【解决方案2】:

AFAICT,你想做这样的事情:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s0="http://schemas.microsoft.com/Sql/2008/05/TypedPolling"
xmlns:ns0="http://NO.Aditro.Schemas.Organization.OrganizationTrees"
exclude-result-prefixes="s0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="children" match="s0:TypedPolling0" use="s0:father" />

<xsl:template match="/">
    <!-- create root node -->
    <ns0:TreeNode>
        <ns0:node>0</ns0:node>
        <ns0:LevelInTree>1</ns0:LevelInTree>
        <!-- get top-level nodes -->
        <xsl:apply-templates select="key('children', 0)">
            <xsl:with-param name="level" select="2" />
        </xsl:apply-templates>
    </ns0:TreeNode>
</xsl:template>

<xsl:template match="s0:TypedPolling0">
    <xsl:param name="level"/>
    <ns0:TreeNode>
        <ns0:node>
            <xsl:value-of select="s0:son" />
        </ns0:node>
        <ns0:LevelInTree>
            <xsl:value-of select="$level" />
        </ns0:LevelInTree>
        <!-- get children -->
        <xsl:apply-templates select="key('children', s0:son)">
            <xsl:with-param name="level" select="$level + 1" />
        </xsl:apply-templates>
    </ns0:TreeNode>
</xsl:template>

</xsl:stylesheet>

当应用于您的输入示例时,结果将是:

<?xml version="1.0" encoding="UTF-8"?>
<ns0:TreeNode xmlns:ns0="http://NO.Aditro.Schemas.Organization.OrganizationTrees">
   <ns0:node>0</ns0:node>
   <ns0:LevelInTree>1</ns0:LevelInTree>
   <ns0:TreeNode>
      <ns0:node>1</ns0:node>
      <ns0:LevelInTree>2</ns0:LevelInTree>
      <ns0:TreeNode>
         <ns0:node>2</ns0:node>
         <ns0:LevelInTree>3</ns0:LevelInTree>
      </ns0:TreeNode>
   </ns0:TreeNode>
   <ns0:TreeNode>
      <ns0:node>3</ns0:node>
      <ns0:LevelInTree>2</ns0:LevelInTree>
      <ns0:TreeNode>
         <ns0:node>4</ns0:node>
         <ns0:LevelInTree>3</ns0:LevelInTree>
      </ns0:TreeNode>
   </ns0:TreeNode>
</ns0:TreeNode>

【讨论】:

    【解决方案3】:
    <?xml version="1.0" encoding="utf-16"?>
    <xsl:stylesheet version="1.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                    xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
                    xmlns:s0="http://schemas.microsoft.com/Sql/2008/05/TypedPolling"
                    xmlns:ns0="http://NO.Aditro.Schemas.Organization.OrganizationTrees"
                    exclude-result-prefixes="msxsl var s0">
    
      <xsl:output omit-xml-declaration="yes" method="xml" indent="yes" version="1.0" />
      <xsl:template match="/">
            <xsl:variable name="FirstNodeSon" select="0" />
            <xsl:variable name="FirstNodelevel" select="1" />
            <ns0:TreeNode>
              <ns0:node>
                <xsl:value-of select="$FirstNodeSon"/>
              </ns0:node>
              <ns0:LevelInTree>
                <xsl:value-of select="$FirstNodelevel"/>
              </ns0:LevelInTree>
              <xsl:apply-templates mode="NewNode" select="s0:TypedPolling/s0:TypedPolling0/s0:TypedPolling0[s0:father=$FirstNodeSon]">
                <xsl:with-param name="level" select="$FirstNodelevel+1" />
              </xsl:apply-templates>
            </ns0:TreeNode>
      </xsl:template>
      <xsl:template name="NewTreeNode" match="s0:TypedPolling0" mode="NewNode">
        <xsl:param name="level" />
        <xsl:variable name="CurrentNodeSon" select="s0:Son" />
        <ns0:TreeNode>
          <ns0:node>
            <xsl:value-of select="s0:Son" />
          </ns0:node>
          <ns0:LevelInTree>
            <xsl:value-of select="$level" />
          </ns0:LevelInTree>
          <xsl:apply-templates select="/s0:TypedPolling/s0:TypedPolling0/s0:TypedPolling0[s0:father=$CurrentNodeSon]" mode="NewNode">
            <xsl:with-param name="level" select="$level+1" />
          </xsl:apply-templates>
        </ns0:TreeNode>
      </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

    • 您对mode 的使用是不必要且令人困惑的。 OTOH,您应该使用key 来获取子节点 - 正如您在 OP 中开始的那样。 --附言我相信如果你解决这个问题并删除所有多余的变量和命名空间声明,你就会得到我发布的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 2011-03-07
    • 2013-06-03
    • 2011-07-23
    相关资源
    最近更新 更多