列出 XML 文件中的每个节点

发布于 2024-09-29 22:49:51 字数 374 浏览 3 评论 0原文

简单的情况...对于任何随机 XML 文件,我想创建它包含的每个节点的列表,但没有任何重复项!所以类似:

<root name="example">
  <child id="1">
    <grandchild/>
  </child>
  <child id="2"/>
  <child id="3"/>
</root>

翻译为:

/root
/root/@name
/root/child
/root/child/@id
/root/child/grandchild

如何仅使用 XSLT 来做到这一点?

Simple situation... With any random XML file, I want to create a list of every node that it contains, but without any duplicates! So something like:

<root name="example">
  <child id="1">
    <grandchild/>
  </child>
  <child id="2"/>
  <child id="3"/>
</root>

Is translated to:

/root
/root/@name
/root/child
/root/child/@id
/root/child/grandchild

How to do this, by just using XSLT?

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

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

发布评论

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

评论(3

许你一世情深 2024-10-06 22:49:51

只是为了好玩,没有扩展功能。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text()"/>
    <xsl:template match="*|@*">
        <xsl:param name="pPath"/>
        <xsl:param name="pNames" select="'
'"/>
        <xsl:variable name="vPath"
                      select="concat($pPath,'/',
                                     substring('@',
                                               1 div (count(.|../@*) =
                                                      count(../@*))),
                                     name())"/>
        <xsl:variable name="vNames">
            <xsl:if test="not(contains($pNames,
                                       concat('
',$vPath,'
')))">
                <xsl:value-of select="concat($vPath,'
')"/>
            </xsl:if>
            <xsl:apply-templates select="*[1]|@*">
                <xsl:with-param name="pPath" select="$vPath"/>
                <xsl:with-param name="pNames" select="$pNames"/>
            </xsl:apply-templates>
        </xsl:variable>
        <xsl:value-of select="$vNames"/>
        <xsl:apply-templates select="following-sibling::*[1]">
            <xsl:with-param name="pPath" select="$pPath"/>
            <xsl:with-param name="pNames" select="concat($pNames,$vNames)"/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

输出:

/root
/root/@name
/root/child
/root/child/@id
/root/child/grandchild

编辑:XSLT/XPath 2.0 的更好示例。此 XPath 2.0 行:

string-join(
   distinct-values(
      (//*|//@*)
         /string-join(
            (ancestor::node()/name(),
             if (self::attribute())
                then concat('@',name())
                else name()),
            '/')),
   '
')

Just for fun, without extension function.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text()"/>
    <xsl:template match="*|@*">
        <xsl:param name="pPath"/>
        <xsl:param name="pNames" select="'
'"/>
        <xsl:variable name="vPath"
                      select="concat($pPath,'/',
                                     substring('@',
                                               1 div (count(.|../@*) =
                                                      count(../@*))),
                                     name())"/>
        <xsl:variable name="vNames">
            <xsl:if test="not(contains($pNames,
                                       concat('
',$vPath,'
')))">
                <xsl:value-of select="concat($vPath,'
')"/>
            </xsl:if>
            <xsl:apply-templates select="*[1]|@*">
                <xsl:with-param name="pPath" select="$vPath"/>
                <xsl:with-param name="pNames" select="$pNames"/>
            </xsl:apply-templates>
        </xsl:variable>
        <xsl:value-of select="$vNames"/>
        <xsl:apply-templates select="following-sibling::*[1]">
            <xsl:with-param name="pPath" select="$pPath"/>
            <xsl:with-param name="pNames" select="concat($pNames,$vNames)"/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

Output:

/root
/root/@name
/root/child
/root/child/@id
/root/child/grandchild

Edit: Better example of XSLT/XPath 2.0. This XPath 2.0 line:

string-join(
   distinct-values(
      (//*|//@*)
         /string-join(
            (ancestor::node()/name(),
             if (self::attribute())
                then concat('@',name())
                else name()),
            '/')),
   '
')
这个俗人 2024-10-06 22:49:51

我自己也解决了!这样:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="fo msxsl">
  <xsl:output method="text" standalone="yes" encoding="UTF-8"/>
  <xsl:param name="Detect">false</xsl:param>
  <xsl:param name="Root"/>
  <xsl:variable name="NewLine" select="'
'"/>
  <xsl:template match="/">
          <xsl:variable name="Nodes">
            <xsl:apply-templates select="/" mode="Loop"/>
          </xsl:variable>
          <xsl:variable name="SortedNodes">
            <xsl:apply-templates select="msxsl:node-set($Nodes)" mode="Nodes">
              <xsl:sort select="." order="ascending" case-order="lower-first" data-type="text"/>
            </xsl:apply-templates>
          </xsl:variable>
          <xsl:apply-templates select="msxsl:node-set($SortedNodes)" mode="Text"/>
  </xsl:template>
  <!-- Elementen. -->
  <xsl:template match="*" mode="Loop">
    <xsl:param name="Node"/>
    <Node>
      <xsl:value-of select="$Node"/>/<xsl:value-of select="name()"/>
    </Node>
    <xsl:apply-templates select="@*" mode="Loop">
      <xsl:with-param name="Node" select="concat($Node, '/', name())"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="*" mode="Loop">
      <xsl:with-param name="Node" select="concat($Node, '/', name())"/>
    </xsl:apply-templates>
  </xsl:template>
  <!-- Attributen. -->
  <xsl:template match="@*" mode="Loop">
    <xsl:param name="Node"/>
    <Node>
      <xsl:value-of select="$Node"/>/@<xsl:value-of select="name()"/>
    </Node>
  </xsl:template>
  <!-- Node. -->
  <xsl:template match="Node" mode="Nodes">
    <xsl:if test="(1=position()) or (preceding-sibling::*[1]/. != .)">
      <Node>
        <xsl:value-of select="."/>
      </Node>
    </xsl:if>
  </xsl:template>
  <xsl:template match="Node" mode="Text"><xsl:value-of select="concat(., $NewLine)"/></xsl:template>
</xsl:stylesheet>

只有 48 行。 :-)

I solved it myself, too! This way:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="fo msxsl">
  <xsl:output method="text" standalone="yes" encoding="UTF-8"/>
  <xsl:param name="Detect">false</xsl:param>
  <xsl:param name="Root"/>
  <xsl:variable name="NewLine" select="'
'"/>
  <xsl:template match="/">
          <xsl:variable name="Nodes">
            <xsl:apply-templates select="/" mode="Loop"/>
          </xsl:variable>
          <xsl:variable name="SortedNodes">
            <xsl:apply-templates select="msxsl:node-set($Nodes)" mode="Nodes">
              <xsl:sort select="." order="ascending" case-order="lower-first" data-type="text"/>
            </xsl:apply-templates>
          </xsl:variable>
          <xsl:apply-templates select="msxsl:node-set($SortedNodes)" mode="Text"/>
  </xsl:template>
  <!-- Elementen. -->
  <xsl:template match="*" mode="Loop">
    <xsl:param name="Node"/>
    <Node>
      <xsl:value-of select="$Node"/>/<xsl:value-of select="name()"/>
    </Node>
    <xsl:apply-templates select="@*" mode="Loop">
      <xsl:with-param name="Node" select="concat($Node, '/', name())"/>
    </xsl:apply-templates>
    <xsl:apply-templates select="*" mode="Loop">
      <xsl:with-param name="Node" select="concat($Node, '/', name())"/>
    </xsl:apply-templates>
  </xsl:template>
  <!-- Attributen. -->
  <xsl:template match="@*" mode="Loop">
    <xsl:param name="Node"/>
    <Node>
      <xsl:value-of select="$Node"/>/@<xsl:value-of select="name()"/>
    </Node>
  </xsl:template>
  <!-- Node. -->
  <xsl:template match="Node" mode="Nodes">
    <xsl:if test="(1=position()) or (preceding-sibling::*[1]/. != .)">
      <Node>
        <xsl:value-of select="."/>
      </Node>
    </xsl:if>
  </xsl:template>
  <xsl:template match="Node" mode="Text"><xsl:value-of select="concat(., $NewLine)"/></xsl:template>
</xsl:stylesheet>

Only 48 lines. :-)

绅刃 2024-10-06 22:49:51

这个转换(133行,其中许多被注释掉):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kPathByVal" match="path" use="."/>

 <xsl:template match="node()|@*">
  <path>
    <xsl:call-template name="buildPath"/>
  </path>
  <xsl:apply-templates select="node()|@*"/>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="vrtfPaths">
   <xsl:apply-templates/>
  </xsl:variable>

  <xsl:variable name="vPaths" select="msxsl:node-set($vrtfPaths)/*"/>

  <xsl:for-each select=
   "$vPaths[generate-id()
           =
            generate-id(key('kPathByVal',.)[1])
           ]
   ">
    <xsl:value-of select="concat(.,'
')"/>
  </xsl:for-each>
 </xsl:template>

<xsl:template name="buildPath">
 <xsl:variable name="pNode" select="."/>
  <xsl:variable name="theResult">
    <xsl:for-each select="$pNode">
    <xsl:variable name="theNode" select="."/>
    <xsl:for-each select=
    "$theNode
    |
     $theNode/ancestor-or-self::node()[..]">
      <xsl:element name="slash">/</xsl:element>
      <xsl:choose>
        <xsl:when test="self::*">
          <xsl:element name="nodeName">
            <xsl:value-of select="name()"/>
          </xsl:element>
        </xsl:when>
        <xsl:otherwise> <!-- This node is not an element -->
          <xsl:choose>
            <xsl:when test="count(. | ../@*) = count(../@*)">
            <!-- Attribute -->
              <xsl:element name="nodeName">
                <xsl:value-of select="concat('@',name())"/>
              </xsl:element>
            </xsl:when>
            <xsl:when test="self::text()">  <!-- Text -->
              <xsl:element name="nodeName">
                <xsl:value-of select="'text()'"/>
                <xsl:variable name="thisPosition"
                          select="count(preceding-sibling::text())"/>
                <xsl:variable name="numFollowing"
                          select="count(following-sibling::text())"/>
                <xsl:if test="$thisPosition + $numFollowing > 0">
                  <xsl:value-of select=
                  "concat('[', $thisPosition +1, ']')"/>
                </xsl:if>
              </xsl:element>
            </xsl:when>
            <xsl:when test="self::processing-instruction()">
            <!-- Processing Instruction -->
              <xsl:element name="nodeName">
                <xsl:value-of select="'processing-instruction()'"/>
                <xsl:variable name="thisPosition"
                   select="count(preceding-sibling::processing-instruction())"/>
                <xsl:variable name="numFollowing"
                    select="count(following-sibling::processing-instruction())"/>
                <xsl:if test="$thisPosition + $numFollowing > 0">
                  <xsl:value-of select=
                  "concat('[', $thisPosition +1, ']')"/>
                </xsl:if>
              </xsl:element>
            </xsl:when>
            <xsl:when test="self::comment()">   <!-- Comment -->
              <xsl:element name="nodeName">
                <xsl:value-of select="'comment()'"/>
                <xsl:variable name="thisPosition"
                         select="count(preceding-sibling::comment())"/>
                <xsl:variable name="numFollowing"
                         select="count(following-sibling::comment())"/>
                <xsl:if test="$thisPosition + $numFollowing > 0">
                  <xsl:value-of select=
                  "concat('[', $thisPosition +1, ']')"/>
                </xsl:if>
              </xsl:element>
            </xsl:when>
            <!-- Namespace: -->
            <xsl:when test=
              "count(. | ../namespace::*)
              =
               count(../namespace::*)">

              <xsl:variable name="apos">'</xsl:variable>
              <xsl:element name="nodeName">
                <xsl:value-of select="concat('namespace::*',
                '[local-name() = ', $apos, local-name(), $apos, ']')"/>

              </xsl:element>
            </xsl:when>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
    <!-- <xsl:text>
</xsl:text> -->
  </xsl:for-each>
 </xsl:variable>
 <xsl:value-of select="$theResult"/>
</xsl:template>

当应用于提供的 XML 文档时:

<root name="example">
  <child id="1">
    <grandchild/>
  </child>
  <child id="2"/>
  <child id="3"/>
</root>

产生所需的正确结果

/root
/root/@name
/root/child
/root/child/@id
/root/child/grandchild

This transformation (133 lines and many of them commented out):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kPathByVal" match="path" use="."/>

 <xsl:template match="node()|@*">
  <path>
    <xsl:call-template name="buildPath"/>
  </path>
  <xsl:apply-templates select="node()|@*"/>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="vrtfPaths">
   <xsl:apply-templates/>
  </xsl:variable>

  <xsl:variable name="vPaths" select="msxsl:node-set($vrtfPaths)/*"/>

  <xsl:for-each select=
   "$vPaths[generate-id()
           =
            generate-id(key('kPathByVal',.)[1])
           ]
   ">
    <xsl:value-of select="concat(.,'
')"/>
  </xsl:for-each>
 </xsl:template>

<xsl:template name="buildPath">
 <xsl:variable name="pNode" select="."/>
  <xsl:variable name="theResult">
    <xsl:for-each select="$pNode">
    <xsl:variable name="theNode" select="."/>
    <xsl:for-each select=
    "$theNode
    |
     $theNode/ancestor-or-self::node()[..]">
      <xsl:element name="slash">/</xsl:element>
      <xsl:choose>
        <xsl:when test="self::*">
          <xsl:element name="nodeName">
            <xsl:value-of select="name()"/>
          </xsl:element>
        </xsl:when>
        <xsl:otherwise> <!-- This node is not an element -->
          <xsl:choose>
            <xsl:when test="count(. | ../@*) = count(../@*)">
            <!-- Attribute -->
              <xsl:element name="nodeName">
                <xsl:value-of select="concat('@',name())"/>
              </xsl:element>
            </xsl:when>
            <xsl:when test="self::text()">  <!-- Text -->
              <xsl:element name="nodeName">
                <xsl:value-of select="'text()'"/>
                <xsl:variable name="thisPosition"
                          select="count(preceding-sibling::text())"/>
                <xsl:variable name="numFollowing"
                          select="count(following-sibling::text())"/>
                <xsl:if test="$thisPosition + $numFollowing > 0">
                  <xsl:value-of select=
                  "concat('[', $thisPosition +1, ']')"/>
                </xsl:if>
              </xsl:element>
            </xsl:when>
            <xsl:when test="self::processing-instruction()">
            <!-- Processing Instruction -->
              <xsl:element name="nodeName">
                <xsl:value-of select="'processing-instruction()'"/>
                <xsl:variable name="thisPosition"
                   select="count(preceding-sibling::processing-instruction())"/>
                <xsl:variable name="numFollowing"
                    select="count(following-sibling::processing-instruction())"/>
                <xsl:if test="$thisPosition + $numFollowing > 0">
                  <xsl:value-of select=
                  "concat('[', $thisPosition +1, ']')"/>
                </xsl:if>
              </xsl:element>
            </xsl:when>
            <xsl:when test="self::comment()">   <!-- Comment -->
              <xsl:element name="nodeName">
                <xsl:value-of select="'comment()'"/>
                <xsl:variable name="thisPosition"
                         select="count(preceding-sibling::comment())"/>
                <xsl:variable name="numFollowing"
                         select="count(following-sibling::comment())"/>
                <xsl:if test="$thisPosition + $numFollowing > 0">
                  <xsl:value-of select=
                  "concat('[', $thisPosition +1, ']')"/>
                </xsl:if>
              </xsl:element>
            </xsl:when>
            <!-- Namespace: -->
            <xsl:when test=
              "count(. | ../namespace::*)
              =
               count(../namespace::*)">

              <xsl:variable name="apos">'</xsl:variable>
              <xsl:element name="nodeName">
                <xsl:value-of select="concat('namespace::*',
                '[local-name() = ', $apos, local-name(), $apos, ']')"/>

              </xsl:element>
            </xsl:when>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
    <!-- <xsl:text>
</xsl:text> -->
  </xsl:for-each>
 </xsl:variable>
 <xsl:value-of select="$theResult"/>
</xsl:template>

when applied on the provided XML document:

<root name="example">
  <child id="1">
    <grandchild/>
  </child>
  <child id="2"/>
  <child id="3"/>
</root>

produces the wanted, correct result:

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