我需要两个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>
我不想成为一个笨蛋,这不仅仅是一个小问题,所以我愿意为答案付费。
谢谢。
答案 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="' '"/>
<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>