使用xslt编码层次结构问题xslt v1将xml转换为xml

发布于 2024-10-08 21:11:40 字数 541 浏览 9 评论 0原文

在我的输入 xml 文件中,我在元素属性“lp”中获得了编码层次结构:

<element lp="1"/>
<element lp="1.1"/>
<element lp="2"/>
<element lp="3"/>
<element lp="3.1" />
<element lp="3.2" />
<element lp="3.2.1" />

如何将此 xml 数据转换为

<element lp="1">
   <element lp="1.1"/>
</element>
<element lp="2"/>
<element lp="3">
   <element lp="3.1"/>
   <element lp="3.2">
      <element lp="3.2.1">
   </element>
</element>

In my input xml file I have got encoded hierarchy in elements attribute "lp":

<element lp="1"/>
<element lp="1.1"/>
<element lp="2"/>
<element lp="3"/>
<element lp="3.1" />
<element lp="3.2" />
<element lp="3.2.1" />

How to transform this xml data to

<element lp="1">
   <element lp="1.1"/>
</element>
<element lp="2"/>
<element lp="3">
   <element lp="3.1"/>
   <element lp="3.2">
      <element lp="3.2.1">
   </element>
</element>

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

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

发布评论

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

评论(2

白况 2024-10-15 21:11:40

使用 XSLT2.0 可能有一种简单的方法可以做到这一点,但我在这里假设是 XSLT1.0。

需要注意的一件事是,您的 XML 并不严格有效,因为它缺少根元素。出于答案的目的,我假设根元素称为 elements

为了实现这一点,我认为您需要一个函数来确定元素的“级别”。这可以通过计算 @lp 属性中句号的数量来完成。在 XSLT1.0 中,我通过从文本中删除所有句号并将生成的字符串长度与原始字符串长度进行比较来完成此操作

<xsl:variable name="level" select="string-length(@lp) - string-length(translate(@lp, '.', ''))" />

因此,为了匹配顶级元素,您将执行此操作...

<xsl:apply-templates 
   select="element[string-length(@lp) - string-length(translate(@lp, '.', '')) = 0]"/>

这将匹配以下元素

<element lp="1."/>
<element lp="2."/>
<element lp="3."/>

Next ,对于每个匹配的元素,都是匹配后面元素的情况,其中

  • @lp 属性以当前 @lp 属性开始
  • level 比当前 level 多一级

这可以通过下面的 select 来完成

<xsl:apply-templates 
   select="following-sibling::element[substring(@lp, 1, $len) = $lp][string-length(@lp) - string-length(translate(@lp, '.', '')) = $level + 1]"/>

(注意 $len 和 $level是包含当前 @lp 属性的长度和当前级别的变量)

将其放在一起给出以下 XSLT....

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

   <xsl:template match="/elements">
      <elements>
         <xsl:apply-templates select="element[string-length(@lp) - string-length(translate(@lp, '.', '')) = 0]"/>
      </elements>
   </xsl:template>

   <xsl:template match="element">
      <xsl:variable name="lp" select="@lp"/>
      <xsl:variable name="len" select="string-length(@lp)"/>
      <xsl:variable name="level" select="$len - string-length(translate(@lp, '.', ''))" />

      <xsl:copy>
         <xsl:copy-of select="@lp"/>
         <xsl:apply-templates select="following-sibling::element[substring(@lp, 1, $len) = $lp][string-length(@lp) - string-length(translate(@lp, '.', '')) = $level + 1]"/>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

当应用于以下 XML 时

<elements>
    <element lp="1"/>
    <element lp="1.1"/>
    <element lp="2"/>
    <element lp="3"/>
    <element lp="3.1"/>
    <element lp="3.2"/>
    <element lp="3.2.1"/>
</elements>

产生以下输出

<elements>
    <element lp="1">
        <element lp="1.1"/>
    </element>
    <element lp="2"/>
    <element lp="3">
        <element lp="3.1"/>
        <element lp="3.2">
            <element lp="3.2.1"/>
        </element>
    </element>
</elements>

There is probably a simply way to do this with XSLT2.0, but I have assumed XSLT1.0 here.

One thing to not is that your XML is not strictly valid, because it lacks a root element. For the purposes of the answer, I have assumed the root element is called elements

To achieve this, I think you need a function to determine the 'level' of an element. This can be done by counting the number of full stops in the @lp attribute. In XSLT1.0 I have done this by removing all full-stops from the text, and comparing the resultant string length with the original string length

<xsl:variable name="level" select="string-length(@lp) - string-length(translate(@lp, '.', ''))" />

Thus to match the top level elements you would do this...

<xsl:apply-templates 
   select="element[string-length(@lp) - string-length(translate(@lp, '.', '')) = 0]"/>

This would match the folllowing elements

<element lp="1."/>
<element lp="2."/>
<element lp="3."/>

Next, for each matched element, it is a case of matching following elements where

  • The @lp attribute begins with the current @lp attribute
  • The level is one more than the current level

This can be done with the following select

<xsl:apply-templates 
   select="following-sibling::element[substring(@lp, 1, $len) = $lp][string-length(@lp) - string-length(translate(@lp, '.', '')) = $level + 1]"/>

(Note $len and $level are variables containing the length of the current @lp attribute and the current level)

Putting this altogether gives the following XSLT....

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

   <xsl:template match="/elements">
      <elements>
         <xsl:apply-templates select="element[string-length(@lp) - string-length(translate(@lp, '.', '')) = 0]"/>
      </elements>
   </xsl:template>

   <xsl:template match="element">
      <xsl:variable name="lp" select="@lp"/>
      <xsl:variable name="len" select="string-length(@lp)"/>
      <xsl:variable name="level" select="$len - string-length(translate(@lp, '.', ''))" />

      <xsl:copy>
         <xsl:copy-of select="@lp"/>
         <xsl:apply-templates select="following-sibling::element[substring(@lp, 1, $len) = $lp][string-length(@lp) - string-length(translate(@lp, '.', '')) = $level + 1]"/>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

When applied to the following XML

<elements>
    <element lp="1"/>
    <element lp="1.1"/>
    <element lp="2"/>
    <element lp="3"/>
    <element lp="3.1"/>
    <element lp="3.2"/>
    <element lp="3.2.1"/>
</elements>

Produces the following output

<elements>
    <element lp="1">
        <element lp="1.1"/>
    </element>
    <element lp="2"/>
    <element lp="3">
        <element lp="3.1"/>
        <element lp="3.2">
            <element lp="3.2.1"/>
        </element>
    </element>
</elements>
农村范ル 2024-10-15 21:11:40

我认为这是之前的答案......这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root">
        <result>
            <xsl:apply-templates select="element[not(contains(@lp,'.'))]"/>
        </result>
    </xsl:template>
    <xsl:template match="element">
    <xsl:variable name="vLevel" select="concat(@lp,'.')"/>
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates
                 select="../element[starts-with(@lp,$vLevel)]
                                   [not(contains(substring-after(@lp,$vLevel),
                                                 '.'))]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

输出:

<result>
    <element lp="1">
        <element lp="1.1"></element>
    </element>
    <element lp="2"></element>
    <element lp="3">
        <element lp="3.1"></element>
        <element lp="3.2">
            <element lp="3.2.1"></element>
        </element>
    </element>
</result>

I think this was answer before... This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root">
        <result>
            <xsl:apply-templates select="element[not(contains(@lp,'.'))]"/>
        </result>
    </xsl:template>
    <xsl:template match="element">
    <xsl:variable name="vLevel" select="concat(@lp,'.')"/>
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates
                 select="../element[starts-with(@lp,$vLevel)]
                                   [not(contains(substring-after(@lp,$vLevel),
                                                 '.'))]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output:

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