XSL产生所有元素和属性名称

时间:2018-11-15 01:11:24

标签: xml xslt

我需要两个XSL样式表来评估任何XML文档并返回:

1)层次结构中的所有元素名称;

2)与每个元素关联的所有属性名称;和

3)一个样式表,将结果输出到XML;一种样式表,将结果输出到文本。

我将在SSIS中对此进行转换,我已经做了很多次,包括使用我创建的基本XSL样式表。我将其加载到数据库中的表中。

请注意,我对XSL / XML知之甚少。我可能使用不正确的术语。另外,我可能会忽略我的要求中明显的内容。因此,我将依靠您来应用您的见解。

示例XML:

<BOOK id="1" chapters="9">
    <AUTHOR gender="Male" age="43">
        <NAME>John Smith</NAME>
    </AUTHOR>
    <TITLE>Just a book</TITLE>
</BOOK>

所需的文本输出(两列,由制表符或某些字符分隔):

element    attribute
/BOOK    id
/BOOK    chapters
/BOOK/AUTHOR    gender
/BOOK/AUTHOR    age
/BOOK/AUTHOR/NAME 
/BOOK/TITLE

所需的XML输出(或多或少?不确定;建议接受)

<ROOT>
    <ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>id</ATTRIBUTE>
    <ELEMENT>/BOOK</ELEMENT><ATTRIBUTE>chapters</ATTRIBUTE>
    <ELEMENT>/BOOK/AUTHOR</ELEMENT>gender<ATTRIBUTE></ATTRIBUTE>
    <ELEMENT>/BOOK/AUTHOR</ELEMENT>age<ATTRIBUTE></ATTRIBUTE>
    <ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
    <ELEMENT>/BOOK/TITLE</ELEMENT>
</ROOT>

我不想成为一个笨蛋,这不仅仅是一个小问题,所以我愿意为答案付费。

谢谢。

2 个答案:

答案 0 :(得分:0)

您可以使用以下样式表生成所需的XML:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output indent="yes"/>

    <xsl:template match="/">
        <ROOT>
        <xsl:apply-templates select="//*[not(@*)]|//@*" />
        </ROOT>
    </xsl:template>

    <xsl:template match="*">
        <ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
    </xsl:template>

    <xsl:template match="@*">
        <xsl:apply-templates select=".." />
        <ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
    </xsl:template>

    <xsl:template match="*" mode="path">
        <xsl:text>/</xsl:text>
        <xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
    </xsl:template>

</xsl:stylesheet>

然后通过将 format 参数的值设置为“ text”来增强它以生成所需的XML或文本输出。将 format 参数值设置为其他任何值都会生成XML输出。您可以通过为 delim 参数设置不同的值来调整定界符值。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output indent="yes" omit-xml-declaration="yes"/>

    <xsl:param name="format" select="'text'"/>

    <xsl:param name="delim" select="'   '"/>

    <xsl:template match="/">
      <!--generate XML report and assign to a variable-->
      <xsl:variable name="report">
        <ROOT>
          <!--Push elements that don't have attributes, and all attributes.
              Elements that have attributes will be transformed when
              transforming the attributes -->
          <xsl:apply-templates select="//*[not(@*)] | //@*" />
        </ROOT>
      </xsl:variable>
      <!--depending upon the value of the format parameter, 
          either transform that XML into the text report,
          or return the generated XML -->  
      <xsl:choose>
        <xsl:when test="$format='text'">
            <xsl:apply-templates select="$report/ROOT"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$report"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

    <xsl:template match="*">
        <ELEMENT><xsl:apply-templates select="." mode="path"/></ELEMENT>
    </xsl:template>

    <!--for each attribute, generate an ELEMENT with the path and an ATTRIBUTE element-->
    <xsl:template match="@*">
        <xsl:apply-templates select=".." />
        <ATTRIBUTE><xsl:value-of select="name()"/></ATTRIBUTE>
    </xsl:template>

    <xsl:template match="*" mode="path">
        <xsl:text>/</xsl:text>
        <xsl:value-of select="ancestor-or-self::*/name(.)" separator="/"/>
    </xsl:template>

    <!--templates to transform the generated XML into the text output -->
    <xsl:template match="ROOT">
        <xsl:sequence select="string-join(('element', 'attribute'), $delim)"/>
        <xsl:apply-templates select="ELEMENT"/>
    </xsl:template>

    <xsl:template match="ELEMENT">
      <xsl:value-of select="'&#10;'"/>
      <xsl:value-of select="string-join((., following-sibling::*[1][self::ATTRIBUTE]), $delim)"/>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

首先,要求:

(a)您的示例没有包含具有相同路径的多个元素或属性的任何实例,因此尚不清楚您是否要消除重复项。

(b)您的输出XML的处理非常棘手,因为除相对位置外,元素路径和属性名称没有连接。最好添加一个包装元素,例如<PATH>

在输出中的

(c)中,元素路径出现的次数为N,其中N为找到的属性数,但是当没有属性时为1。这对我来说似乎有点矛盾。我建议输出表格

<ROOT>
  <PATH>    
    <ELEMENT>/BOOK</ELEMENT>
    <ATTRIBUTE>id</ATTRIBUTE>
    <ATTRIBUTE>chapters</ATTRIBUTE>
  </PATH>
  <PATH>
    <ELEMENT>/BOOK/AUTHOR</ELEMENT>
    <ATTRIBUTE>gender<ATTRIBUTE>
    <ATTRIBUTE>age</ATTRIBUTE>
  <PATH>
    <ELEMENT>/BOOK/AUTHOR/NAME</ELEMENT>
  </PATH>
  <PATH>
    <ELEMENT>/BOOK/AUTHOR/TITLE</ELEMENT>
  </PATH>
</ROOT>

如果我正确地认为您要消除重复项,那么这是一个分组问题,因此在XSLT 2.0+中容易得多。您实际上并未声明对XSLT版本的任何限制,但是我将假设XSLT 2.0。请注意,即使2.0已经问世十多年,仍有许多XSLT处理器仅支持1.0。

首先,我们需要一个提供元素路径的函数:

<xsl:function name="f:path" as="xs:string">
  <xsl:param name="node" as="node()"/>
  <xsl:choose>
    <xsl:when test="exists($node/..)">
      <xsl:sequence select="concat(f:path($node/..), '/', local-name($node)"/>
    </xsl:when>
    <xsl:otherwise>/</xsl:otherwise>
  </xsl:choose>
</xsl:function>

现在分组开始:

<xsl:for-each-group select="//*" group-by="f:path(.)">
  <PATH>
    <ELEMENT><xsl:value-of select="current-grouping-key()"/></ELEMENT>
    <xsl:for-each-group select="current-group()/@*" group-by="local-name()">
      <ATTRIBUTE><xsl:value-of select="local-name()"/></ATTRIBUTE>
    </xsl:for-each-group>
  </PATH>
</xsl:for-each-group>
相关问题