使用 XSLT 转换特定 XML 文档

发布于 2024-11-10 12:26:28 字数 1942 浏览 0 评论 0原文

您好,

我的 XML 文档具有以下布局

<root>
  <child>
    <item type="ID">id1</item>
    <item type="TEXT">text1</item>
  </child>

  <child>
    <item type="ID"/>
    <item type="TEXT">text2</item>
  </child>

  <child>
    <item type="ID"/>
    <item type="TEXT">text3</item>
  </child>

  <child>
    <item type="ID">id2</item>
    <item type="TEXT">text4</item>
  </child>

  <child>
    <item type="ID"/>
    <item type="TEXT">text5</item>
  </child>

  ...
</root>

每次出现 type=ID 的空项目标记时,这意味着它与前面的同级具有相同的值。现在我想将其转换为

<root>
  <child id="id1">text1text2text3</child>
  <child id="id2">text4text5</child>

  ...
</root>

我的解决方案,

<?xml version="1.0"?>

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

  <xsl:template match="/">
    <root>
      <xsl:apply-templates select="//child/item[@type='ID'][text()='id1' or text()='id2']"/>
    </root>
  </xsl:template>

  <xsl:template match="child/item[text()='id1' or text()='id2']">
    <child id="{text()}">
      <xsl:value-of select="./../item[@type='TEXT']/."/>
      <xsl:apply-templates select="./../following::*[1]/item[@type='ID'][not(text()='id1' or text()='id2')]"/>
    </child>
  </xsl:template>

  <xsl:template match="child/item[not(text()='id1' or text()='id2')]">
    <xsl:value-of select="./../item[@type='TEXT']/."/>
    <xsl:apply-templates select="./../following::*[1]/item[@type='ID'][not(text()='id1' or text()='id2')]"/>
  </xsl:template>

</xsl:stylesheet>

它有效,但丑陋且不灵活。例如,如果我有任意 id 值,而不仅仅是 id1 和 id2,该怎么办?有人有好的建议或更好的解决方案吗?

HI

my XML documents have the following layout

<root>
  <child>
    <item type="ID">id1</item>
    <item type="TEXT">text1</item>
  </child>

  <child>
    <item type="ID"/>
    <item type="TEXT">text2</item>
  </child>

  <child>
    <item type="ID"/>
    <item type="TEXT">text3</item>
  </child>

  <child>
    <item type="ID">id2</item>
    <item type="TEXT">text4</item>
  </child>

  <child>
    <item type="ID"/>
    <item type="TEXT">text5</item>
  </child>

  ...
</root>

Everytime there is an empty item tag with type=ID, it means that it has the same value as the preceding sibling. Now i want to transform this into

<root>
  <child id="id1">text1text2text3</child>
  <child id="id2">text4text5</child>

  ...
</root>

My solution to this is

<?xml version="1.0"?>

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

  <xsl:template match="/">
    <root>
      <xsl:apply-templates select="//child/item[@type='ID'][text()='id1' or text()='id2']"/>
    </root>
  </xsl:template>

  <xsl:template match="child/item[text()='id1' or text()='id2']">
    <child id="{text()}">
      <xsl:value-of select="./../item[@type='TEXT']/."/>
      <xsl:apply-templates select="./../following::*[1]/item[@type='ID'][not(text()='id1' or text()='id2')]"/>
    </child>
  </xsl:template>

  <xsl:template match="child/item[not(text()='id1' or text()='id2')]">
    <xsl:value-of select="./../item[@type='TEXT']/."/>
    <xsl:apply-templates select="./../following::*[1]/item[@type='ID'][not(text()='id1' or text()='id2')]"/>
  </xsl:template>

</xsl:stylesheet>

It works but is ugly and unflexible. For example what if i had arbitrary id values and not only id1 and id2. Does anybody has a good advice or a better solution?

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

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

发布评论

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

评论(3

月牙弯弯 2024-11-17 12:26:28

这个 XSLT 1.0 样式表应该可以做到这一点:

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

    <xsl:output indent="yes"/>

    <xsl:template match="/*">
        <root>
            <!-- visit each "child" element with a non-blank ID -->
            <xsl:for-each select="child[item[@type='ID'] != '']">
                <xsl:variable name="this-id" select="item[@type='ID']"></xsl:variable>
                <child id="{$this-id}">
                    <!-- visit this node and each following "child" element which
                        1. has a blank ID, and
                        2. whose immediate preceding non-blank ID child element is this one -->                        
                    <xsl:for-each select=".|following-sibling::child
                        [item[@type='ID'] = '']
                        [preceding-sibling::child[item[@type='ID'] != ''][1]/item[@type='ID']=$this-id]">
                        <xsl:value-of select="item[@type='TEXT']"/>
                    </xsl:for-each>
                </child>

            </xsl:for-each>
        </root>
    </xsl:template>    

</xsl:stylesheet>

This XSLT 1.0 stylesheet should do it:

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

    <xsl:output indent="yes"/>

    <xsl:template match="/*">
        <root>
            <!-- visit each "child" element with a non-blank ID -->
            <xsl:for-each select="child[item[@type='ID'] != '']">
                <xsl:variable name="this-id" select="item[@type='ID']"></xsl:variable>
                <child id="{$this-id}">
                    <!-- visit this node and each following "child" element which
                        1. has a blank ID, and
                        2. whose immediate preceding non-blank ID child element is this one -->                        
                    <xsl:for-each select=".|following-sibling::child
                        [item[@type='ID'] = '']
                        [preceding-sibling::child[item[@type='ID'] != ''][1]/item[@type='ID']=$this-id]">
                        <xsl:value-of select="item[@type='TEXT']"/>
                    </xsl:for-each>
                </child>

            </xsl:for-each>
        </root>
    </xsl:template>    

</xsl:stylesheet>

许仙没带伞 2024-11-17 12:26:28

XSLT 2.0解决方案:

<xsl:template match="root">
  <xsl:for-each-group select="child" 
                      group-starting-with="child[string(item[@type='ID'])]">
    <child id="{item[@type='ID']}">
      <xsl:value-of select="current-group()/item[@type='TEXT']" separator=""/>
    </child>
  </xsl:for-each-group>
</xsl:template>

XSLT 2.0 solution:

<xsl:template match="root">
  <xsl:for-each-group select="child" 
                      group-starting-with="child[string(item[@type='ID'])]">
    <child id="{item[@type='ID']}">
      <xsl:value-of select="current-group()/item[@type='TEXT']" separator=""/>
    </child>
  </xsl:for-each-group>
</xsl:template>
一人独醉 2024-11-17 12:26:28

此 XSLT 1.0 转换

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kFollowing" match="item[@type='TEXT']"
      use="((..|../preceding-sibling::child)
                       /item[@type='ID'and string(.)]
           )[last()]"/>

 <xsl:template match="/*">
  <root>
   <xsl:apply-templates select=
       "*[item[@type='ID' and string(.)]]"/>
  </root>
 </xsl:template>

 <xsl:template match="*">
  <child id="{item[@type='ID']}">
   <xsl:copy-of select="key('kFollowing', item[@type='ID'])/text()"/>
  </child>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<root>
    <child>
        <item type="ID">id1</item>
        <item type="TEXT">text1</item>
    </child>
    <child>
        <item type="ID"/>
        <item type="TEXT">text2</item>
    </child>
    <child>
        <item type="ID"/>
        <item type="TEXT">text3</item>
    </child>
    <child>
        <item type="ID">id2</item>
        <item type="TEXT">text4</item>
    </child>
    <child>
        <item type="ID"/>
        <item type="TEXT">text5</item>
    </child>  ...
</root>

产生所需的正确结果

<root>
   <child id="id1">text1text2text3</child>
   <child id="id2">text4text5</child>
</root>

说明:使用键来表达所有匹配的 item 元素与前面带有 @type='ID' 的第一个 item 和子文本节点之间的关系。

This XSLT 1.0 transformation:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kFollowing" match="item[@type='TEXT']"
      use="((..|../preceding-sibling::child)
                       /item[@type='ID'and string(.)]
           )[last()]"/>

 <xsl:template match="/*">
  <root>
   <xsl:apply-templates select=
       "*[item[@type='ID' and string(.)]]"/>
  </root>
 </xsl:template>

 <xsl:template match="*">
  <child id="{item[@type='ID']}">
   <xsl:copy-of select="key('kFollowing', item[@type='ID'])/text()"/>
  </child>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <child>
        <item type="ID">id1</item>
        <item type="TEXT">text1</item>
    </child>
    <child>
        <item type="ID"/>
        <item type="TEXT">text2</item>
    </child>
    <child>
        <item type="ID"/>
        <item type="TEXT">text3</item>
    </child>
    <child>
        <item type="ID">id2</item>
        <item type="TEXT">text4</item>
    </child>
    <child>
        <item type="ID"/>
        <item type="TEXT">text5</item>
    </child>  ...
</root>

produces the wanted, correct result:

<root>
   <child id="id1">text1text2text3</child>
   <child id="id2">text4text5</child>
</root>

Explanation: Using a key to express the relationship between all matched item elements to the first preceding item with @type='ID' and a child text node.

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