使用 XSLT 从 XML 节点列表创建 HTML

发布于 2024-10-23 11:47:53 字数 496 浏览 0 评论 0原文

我是 XSLT 的菜鸟。 我有一个 XML,其中 t 节点后跟其他节点,然后另一个 t 节点可能会再次出现,后跟节点,依此类推

<t />
<n1 />
<n2 />
..

<t/>
<n3 />
<n4 />
...

我需要将此 XML 转换为 HTML,其中 t 节点将其后面的所有节点包装起来下一个 t 节点

<div class='t'>
   <div class='n1'/>
   <div class='n2'/>
    ...
</div>

<div class='t'>
   <div class='n3'/>
   <div class='n4'/>
    ...
</div>

我很难实现这个。 有什么想法\提示吗?

谢谢!

I am a noob on XSLT.
I have a XML where t nodes are followed by other nodes, and then another t node might appear again followed by nodes again, and so on

<t />
<n1 />
<n2 />
..

<t/>
<n3 />
<n4 />
...

What I need to turn this XML into is a HTML where t nodes wraps all nodes following it up to the next t node

<div class='t'>
   <div class='n1'/>
   <div class='n2'/>
    ...
</div>

<div class='t'>
   <div class='n3'/>
   <div class='n4'/>
    ...
</div>

I am having a hard time implementing this.
Any ideas \ hints?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半山落雨半山空 2024-10-30 11:47:53

这是对相邻的进行分组。有很多解决方案:

通过这个格式良好的输入:

<root>
    <t />
    <n1 />
    <n2 />
    <t/>
    <n3 />
    <n4 />
</root>

XSLT 1.0:跟随轴移动

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]" mode="group"/>
            <xsl:apply-templates select="t"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="t">
        <div class="t">
            <xsl:apply-templates select="following-sibling::node()[1]"
                                 mode="group"/>
        </div>
    </xsl:template>
    <xsl:template match="t" mode="group"/>
    <xsl:template match="node()" mode="group">
        <xsl:apply-templates select="."/>
        <xsl:apply-templates select="following-sibling::node()[1]"
                             mode="group"/>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

XSLT 1.0:按键

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kNodeByMark"
             match="node()[../t][not(self::t)]"
             use="generate-id((..|preceding-sibling::t[1])[last()])"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="key('kNodeByMark',generate-id())"/>
            <xsl:for-each select="t">
                <div class="t">
                    <xsl:apply-templates
                         select="key('kNodeByMark',generate-id())"/>
                </div>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

XSLT 2.0:for-each-group 指令

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="node()[../t[1] >> .]"/>
            <xsl:for-each-group select="node()" group-starting-with="t">
                <div class="t">
                    <xsl:apply-templates 
                         select="current-group()[position()>1]"/>
                </div>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<root>
    <div class="t">
        <div class="n1" />
        <div class="n2" />
    </div>
    <div class="t">
        <div class="n3" />
        <div class="n4" />
    </div>
</root>

编辑:跟随轴移动轴重构为看起来像其他解决方案。剥离身份规则。

This is grouping adjacents. There are many solutions:

Whit this wellformed input:

<root>
    <t />
    <n1 />
    <n2 />
    <t/>
    <n3 />
    <n4 />
</root>

XSLT 1.0: traversing with following axis

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]" mode="group"/>
            <xsl:apply-templates select="t"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="t">
        <div class="t">
            <xsl:apply-templates select="following-sibling::node()[1]"
                                 mode="group"/>
        </div>
    </xsl:template>
    <xsl:template match="t" mode="group"/>
    <xsl:template match="node()" mode="group">
        <xsl:apply-templates select="."/>
        <xsl:apply-templates select="following-sibling::node()[1]"
                             mode="group"/>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

XSLT 1.0: Keys

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kNodeByMark"
             match="node()[../t][not(self::t)]"
             use="generate-id((..|preceding-sibling::t[1])[last()])"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="key('kNodeByMark',generate-id())"/>
            <xsl:for-each select="t">
                <div class="t">
                    <xsl:apply-templates
                         select="key('kNodeByMark',generate-id())"/>
                </div>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

XSLT 2.0: for-each-group instruction

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates select="node()[../t[1] >> .]"/>
            <xsl:for-each-group select="node()" group-starting-with="t">
                <div class="t">
                    <xsl:apply-templates 
                         select="current-group()[position()>1]"/>
                </div>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[starts-with(name(),'n')]">
        <div class="{name()}"/>
    </xsl:template>
</xsl:stylesheet>

Output:

<root>
    <div class="t">
        <div class="n1" />
        <div class="n2" />
    </div>
    <div class="t">
        <div class="n3" />
        <div class="n4" />
    </div>
</root>

EDIT: Traversing following axis refactored to look like the others solutions. Stripping identity rules.

云淡风轻 2024-10-30 11:47:53

请参阅我对您的问题的注释,关于“哪个 XSLT 版本?”。如果您的目标版本支持分组,请参阅此处的其他答案,因为这更容易理解,并且几乎肯定会在任何 XSLT 处理器上表现更好。如果您不确定,我建议您使用像这样的 1.0 解决方案。

您可以使用“XML 片段”来完成此操作,就像您使用大多数 XSLT 处理器发布的那样,但我在 XML 中添加了一个“根”元素,以减少回答您的问题时的某些未知因素。

在下面的解决方案中,我尝试保持 XSLT 的形状和您想要的输出的形状之间的直接关联。在我看来,这使得维护/理解变得更容易,至少对于较小的样式表来说是这样。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/root">
    <xsl:for-each select="t">
      <div class='t'>
        <xsl:for-each select="following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]">
          <div class='{name()}' />
        </xsl:for-each>
      </div>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

“following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)] 的右侧我确信,可以使用类似“current()::position()”(这是无效的,仅供参考)之类的东西来简化,但我生疏了,记不起一些别名语法。

这基本上是说:1)评估每个T。2)选择前面有相同数量T的元素,作为我们当前评估的T的索引。

请注意,您可能尝试过按程序进行迭代,并发现无法存储在 XSLT 中找到的最后一个值。或者您发现可以,但只能使用嵌套模板。您正在执行的这种相同类型的枢轴让许多 XSLT 新手遇到了障碍,所以不要感到难过。

See my note on your question, regarding "which XSLT version?". If grouping is supported in your target version, see other answers here, as that is easier to understand and will almost certainly perform better on any XSLT processor. If you aren't certain, I recommend going with a 1.0 solution like this one.

You can do it with the "XML fragment" exactly like you posted with most XSLT processors, but I added a "root" element to your XML, to reduce certain unknowns in answering your question.

In this solution below, I've tried to keep a direct correlation between the shape of the XSLT and the shape of the output you desire. In my opinion that makes it easier to maintain/understand, at least for smaller stylesheets.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/root">
    <xsl:for-each select="t">
      <div class='t'>
        <xsl:for-each select="following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]">
          <div class='{name()}' />
        </xsl:for-each>
      </div>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

The right-hand side of "following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]" could be simplified, I'm sure, using something like "current()::position()" (which isn't valid, fyi), but I'm rusty and couldn't remember some of the alias syntax.

This basically says: 1) Evaluate every T. 2) Select elements with the same quantity of T preceding them, as the index of the T we are currently evaluating.

Note that you've probably tried iterating through procedurally, and found you can't store the last value found in XSLT. Or you've found that you can, but only with nested templates. This same type of pivot you are performing has many XSLT neophytes hitting roadblocks, so don't feel bad.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文