如何使用 XSL 在 XML 转换中查找随机元素

Posted

技术标签:

【中文标题】如何使用 XSL 在 XML 转换中查找随机元素【英文标题】:How to find random elements in XML transformation using XSL 【发布时间】:2019-11-21 22:24:01 【问题描述】:

我在转换 XML 时遇到了一个问题,比如我无法以正确的顺序获取元素。这些元素是随机的,无法预测它们的顺序。

这是我的 XML

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<toc>
    <layout>
        <header>Item 1</header>
        <tocItem>item one - a</tocItem>
        <tocItem>item one - b</tocItem>
        <header>Item 2</header>
        <tocItem>item two - a</tocItem>
        <tocItem>item two - b</tocItem>
        <tocItem>item two - c</tocItem>
        <tocItem>item two - d</tocItem>
        <tocItem>item two - e</tocItem>
        <header>Item 3</header>
        <tocItem>item three - a</tocItem>
        <header>Item 4</header>
        <tocItem>item four - a</tocItem>
        <tocItem>item four - b</tocItem>
        <tocItem>item four - c</tocItem>
        <header>Item 5</header>
        <tocItem>item five - a</tocItem>
        <tocItem>item five - b</tocItem>
    </layout>
    <layout>
        <header>Item 1</header>
        <tocItem>item one - a</tocItem>
        <tocItem>item one - b</tocItem>
        <header>Item 2</header>
        <tocItem>item two - a</tocItem>  
    </layout>
    <layout>
        <header>Item 1</header>
        <tocItem>item one - a</tocItem>
        <tocItem>item one - b</tocItem>
        <tocItem>item one - c</tocItem>
        <tocItem>item one - d</tocItem>
        <tocItem>item one - e</tocItem>
        <header>Item 2</header>
        <tocItem>item two - c</tocItem>
        <tocItem>item two - d</tocItem>
        <tocItem>item two - e</tocItem>
        <header>Item 4</header>
        <tocItem>item four - a</tocItem>
        <tocItem>item four - b</tocItem>
        <header>Item 5</header>
        <tocItem>item five - a</tocItem>  
    </layout>
</toc>

XSL 在这里

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="/">

            <html lang="en">
            <head>
                <meta charset="UTF-8" />
                <title>Title</title>
            </head>

            <body>
                <div class="toc">
                    <xsl:for-each select="/toc/layout">
                    <div class="layout">                    
                        <xsl:for-each select="/toc/layout/header">
                            <div class="header">
                                <p><xsl:value-of select="header" /></p>
                            </div>
                        </xsl:for-each>
                        <xsl:for-each select="/toc/layout/tocItem">
                            <div class="tocItem">
                                <p><xsl:value-of select="tocItem" /></p>
                            </div>
                        </xsl:for-each>                    
                    </div>  
                    </xsl:for-each>              
                </div>
            </body>

            </html>
    </xsl:template>
 </xsl:stylesheet>

当我尝试上述方法时,它只是重复第一个标题元素和第一个 tocItems。在布局 div 中尝试此代码 &lt;xsl:value-of select="." /&gt; 时,我得到了所有元素。我的目标是一个接一个地获取它们。如下所示。

项目 1

项目一 - 一个

项目一 - b

项目 2

项目二 - 一个

项目二 - b

项目二 - c

【问题讨论】:

我不明白你想在这里完成什么。输入的顺序似乎非常精确。请在给定示例中显示准确和完整的预期结果,并解释为获得它需要应用的逻辑。 P.S.您在&lt;xsl:for-each select="/toc/layout"&gt; 中有&lt;xsl:for-each select="/toc/layout"&gt;。这没有任何意义。 一般来说,如果您只想将所有这些元素转换为 HTML div,并根据元素名称使用 class 属性,那么一个模板就足够了;并且为了保留输入顺序,最好或至少最简单地简单地应用模板。有关示例,请参阅xsltfiddle.liberty-development.net/ncdD7ne。但是,我不确定您是否还想将项目包装在标题之后或嵌套它们,那么可能需要做更多的工作。 @michael.hor257k,对不起,这是我的错误。我刚刚做了一个编辑。虽然我没有那么多的编程技能,只是按照 w3 学校的 XML 教程自己做。 @MartinHonnen,这正是我想要实现的,谢谢。 【参考方案1】:

一般来说,如果您只想将所有这些元素转换为 HTML div,并根据元素名称使用 class 属性,那么一个模板就足够了:

  <xsl:template match="toc | layout | header | tocItem">
      <div class="local-name()">
          <xsl:apply-templates/>
      </div>
  </xsl:template>

为了保持输入顺序,最好或至少最简单的是简单地apply-templates

一个例子是

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="html" indent="no" html-version="5"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>.NET XSLT Fiddle Example</title>
      </head>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="toc | layout | header | tocItem">
      <div class="local-name()">
          <xsl:apply-templates/>
      </div>
  </xsl:template>

</xsl:stylesheet>

使用 XSLT 3 only 声明 &lt;xsl:mode on-no-match="shallow-copy"/&gt; 将身份转换设置为默认处理,但在早期版本中,您可以简单地将其拼写为

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

请参阅https://xsltfiddle.liberty-development.net/ncdD7ne 在线试用。

【讨论】:

【参考方案2】:

我没有检查你想要达到的目标,但你的xsl:for-each 显然是错误的。当您编写&lt;xsl:for-each select="/toc/layout"&gt; 时,for-each 中的上下文节点是&lt;layout&gt; 元素,并且人们会期望进一步的选择是相对于该元素的,而不是来自文档根的绝对路径。我不明白为什么你首先有两个级别的xsl:for-each

【讨论】:

那是我的错误。看来 Martin Honnen 的小提琴帮助了我。顺便说一句,感谢您回答问题。

以上是关于如何使用 XSL 在 XML 转换中查找随机元素的主要内容,如果未能解决你的问题,请参考以下文章

在 XML 中查找重复值并在 XSL 中删除节点

XML 和 XSL 连接

XSLT转换无法模板匹配

如何使用 XSL 在 XML 中的特定 td 中保留新行和空白

如何使用 XSL 将 XML 转换为 CSV

使用从 xml 到 html 的 xsl 转换来维护空格和换行符