【问题标题】:XSLT to wrap all chid elements in parent tagXSLT 将所有子元素包装在父标签中
【发布时间】:2013-07-31 08:15:29
【问题描述】:

我有一个这样的 xml 文档:

<Catalogs>
    <Catalog>
        <Code>x</Code>
        <Name>Ox</Name>
        <Categories>
            <Category>
                <Id>9245</Id>
                <Name>a</Name>
                <Category>
                    <Id>9247</Id>
                    <Name>x</Name>
                </Category>
            </Category>
            <Category>
                <Id>9250</Id>
                <Name>x</Name>
                <Category>
                    <Id>9252</Id>
                    <Name>x</Name>
                </Category>
                <Category>
                    <Id>9258</Id>
                    <Name>x</Name>
                    <Category>
                        <Id>9260</Id>
                        <Name>x</Name>
                    </Category>
                    <Category>
                        <Id>9261</Id>
                        <Name>x</Name>
                    </Category>
                    <Category>
                        <Id>9261</Id>
                        <Name>x</Name>
                    </Category>
                </Category>
            </Category>
            <Category>
                <Id>9251</Id>
                <Name>x</Name>
                <Category>
                    <Id>9253</Id>
                    <Name>x</Name>
                </Category>
            </Category>
        </Categories>
    </Catalog>
</Catalogs>

我想将类别标签的每个子集包装成一个集合标签(类别)。 这里的问题是,这是一棵递归树,树的深度是未知的。

我尝试为此使用 xslt 转换,但尚未成功。我的尝试

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="Category">
  <Categories><xsl:apply-templates select="Category"/></Categories>
</xsl:template>

</xsl:stylesheet>

只是用一个空的类别标签替换所有的孩子。

示例输出应该是这样的:

<Catalogs>
    <Catalog>
        <Code>x</Code>
        <Name>Ox</Name>
        <Categories>
            <Category>
                <Id>9245</Id>
                <Name>a</Name>
                <Categories>
                    <Category>
                        <Id>9247</Id>
                        <Name>x</Name>
                    </Category>
                </Categories>
            </Category>
            <Category>
                <Id>9250</Id>
                <Name>x</Name>
                <Categories>
                    <Category>
                        <Id>9252</Id>
                        <Name>x</Name>
                    </Category>
                    <Category>
                        <Id>9258</Id>
                        <Name>x</Name>
                        <Categories>
                            <Category>
                                <Id>9260</Id>
                                <Name>x</Name>
                            </Category>
                            <Category>
                                <Id>9261</Id>
                                <Name>x</Name>
                            </Category>
                            <Category>
                                <Id>9261</Id>
                                <Name>x</Name>
                            </Category>
                        </Categories>
                    </Category>
                </Categories>
            </Category>
            <Category>
                <Id>9251</Id>
                <Name>x</Name>
                <Categories>
                    <Category>
                        <Id>9253</Id>
                        <Name>x</Name>
                    </Category>
                </Categories>
            </Category>
        </Categories>
    </Catalog>
</Catalogs>

任何指针(或完整的解决方案)将不胜感激。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    首先,您应该在 XSLT 身份模板之上构建您的 XSLT,这将复制您的 XML 中没有显式匹配模板的所有节点

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

    没有这个,XSLT 的内置模板将启动,并输出它找到的任何元素的文本。

    XSLT 的另一个问题是,当您匹配 Category 元素时,您正在执行&lt;xsl:apply-templates select="Category"/&gt;,这意味着您只是告诉 XSLT 查找任何 Category 作为当前元素的子元素的元素。

    您可以采取的一种方法是让您的模板匹配任何 Category 元素的父级(已排除 Categories 元素)

    <xsl:template match="*[not(self::Categories)][Category]">
    

    然后,您可以在其中复制该元素,并在其中插入一个 Categories 元素以包含所有 Category 元素

    <xsl:copy>
      <xsl:apply-templates select="@*|node()[not(self::Category)]"/>
      <Categories>
        <xsl:apply-templates select="Category"/>
      </Categories>
    </xsl:copy>
    

    这是本例中的完整 XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match="*[not(self::Categories)][Category]">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()[not(self::Category)]"/>
          <Categories>
            <xsl:apply-templates select="Category"/>
          </Categories>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
    

    这种方法的一个缺点是,如果您有任何元素出现在父级中的 Category 之后,这些元素将被移到创建的 Categories 元素之前.

    另一种方法是匹配父元素中第一次出现的 Category 元素,然后复制该元素及其所有后续兄弟元素

    也试试这个 XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" indent="yes"/>
    
      <xsl:template match="Category[1]">
        <Categories>
          <xsl:apply-templates select="self::*|following-sibling::Category" mode="categories"/>
        </Categories>
      </xsl:template>
    
      <xsl:template match="Category" mode="categories">
        <xsl:call-template name="identity" />
      </xsl:template>
    
      <xsl:template match="Category" />
    
      <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
    

    关于这种方法有两点需要注意。首先,XSLT 在寻找与节点匹配的模板时,总是优先考虑更具体的模板匹配。因此,“Category[1]”将被用于第一个子 Category 元素的“Category”。

    其次,请注意此处使用 mode,否则您将有两个模板匹配具有相同优先级的“Category”,这是不允许的。

    【讨论】:

    • 不仅感谢 2 (!) 个有效的解决方案,还感谢一些额外的解释。看来我需要更多地了解 xslt
    猜你喜欢
    • 2021-06-27
    • 2020-07-29
    • 1970-01-01
    • 2015-02-16
    • 2010-09-19
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多