多个 SSRS 报告的统一 xslt(xml 导出)
Unified xslt for multiple SSRS reports (xml export)
我有 SSRS 2016 和一些报告(结构相似但名称和数据不同)要在 xml 导出期间进行转换。
报告xml示例:
<?xml version="1.0" encoding="utf-8"?>
<Report xsi:schemaLocation="My_Report_1 http://..." Name="My_Report_1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="My_Report_1">
<Table>
<tbl_MainGroup_Collection>
<Data TxDev="abc" DateTime="20200906001057" DBVersion="1.0" ProductVersion="1.0" Version="1.0">
<tbl_SiteGroup_Collection>
<Site SiteName="All sites">
<tbl_ColumnGroup_Collection>
<Column Column1="Week 33" Column2="Week 34">
<Lines>
<Line LineName="abc" Column1="0.00" Column2="0.00" LineNumber="1" EntityType="0" EntityNo="1" />
<Line LineName="dfg" LineNumber="-1" EntityType="0" EntityNo="2" />
</Lines>
</Column>
</tbl_ColumnGroup_Collection>
</Site>
</tbl_SiteGroup_Collection>
</Data>
</tbl_MainGroup_Collection>
</Table>
</Report>
XSLT 转换示例:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0"
xmlns:x="My_Report_1"
exclude-result-prefixes="x"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml" />
<xsl:template match="x:Report|x:Table|x:tbl_MainGroup_Collection">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="x:Report/x:Table//x:Data">
<Data TxDev="{@TxDev}" DateTime="{@DateTime}" DBVersion="{@DBVersion}" ProductVersion="{@ProductVersion}" Version="{@Version}">
<xsl:apply-templates select="x:tbl_SiteGroup_Collection"/>
</Data>
</xsl:template>
<xsl:template match="x:tbl_SiteGroup_Collection">
<Sites>
<xsl:apply-templates select="x:Site"/>
</Sites>
</xsl:template>
<xsl:template match="x:Site">
<Site SiteNumber="{@SiteNumber}" SiteID="{@SiteID}" SiteName="{@SiteName}">
<xsl:apply-templates select="x:tbl_ColumnGroup_Collection"/>
</Site>
</xsl:template>
<xsl:template match="x:tbl_ColumnGroup_Collection">
<Columns>
<xsl:apply-templates select="x:Column"/>
</Columns>
</xsl:template>
<xsl:template match="x:Column">
<xsl:for-each select="@*[starts-with(local-name(), 'Column')]">
<Column ColumnNumber="{translate(local-name(),'Column','')}" ColumnName="{.}">
<xsl:variable name="attrName" select="local-name()"/>
<xsl:for-each select="../x:Lines">
<xsl:call-template name="Lines">
<xsl:with-param name="ColumnName" select="$attrName"/>
</xsl:call-template>
</xsl:for-each>
</Column>
</xsl:for-each>
</xsl:template>
<xsl:template name="Lines" match="x:Lines">
<xsl:param name="ColumnName"/>
<Lines>
<xsl:for-each select="x:Line[@LineNumber>0 and @EntityType=0]">
<xsl:sort select="@LineNumber" data-type="number" />
<xsl:call-template name="Line">
<xsl:with-param name="ColumnName" select="$ColumnName"/>
</xsl:call-template>
</xsl:for-each>
</Lines>
</xsl:template>
<xsl:template name="Line" match="x:Line">
<xsl:param name="ColumnName"/>
<Line LineNumber="{@LineNumber}" LineName="{@LineName}" >
<xsl:variable name="LineNo" select="@LineNumber"/>
<xsl:choose>
<xsl:when test="count(../x:Line[@LineNumber=$LineNo and @EntityType=1])>0">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
<Departments>
<xsl:for-each select="../x:Line[@LineNumber=$LineNo and @EntityType=1]">
<xsl:sort select="@EntityNo" data-type="number"/>
<Department DepNumber="{@EntityNo}" DepName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</Department>
</xsl:for-each>
</Departments>
</xsl:when>
<xsl:when test="count(../x:Line[@LineNumber=$LineNo and @EntityType=2])>0">
<xsl:if test="@*[local-name()=$ColumnName]">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:if>
<MOPs>
<xsl:for-each select="../x:Line[@LineNumber=$LineNo and @EntityType=2]">
<xsl:sort select="@EntityNo" data-type="number"/>
<MOP MOPNumber="{@EntityNo}" MOPName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</MOP>
</xsl:for-each>
</MOPs>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</Line>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
由于命名空间绑定 xmlns:x="My_Report_1" 我只能将此 xslt 转换用于此报告。有没有什么方法可以生成更通用的 xslt 转换来处理所有此类报告,而不管 namespace-uri 的值如何?为命名空间值提供参数?
提前致谢。
来自 OP 的评论:
What I want is to pass some kind of variable or function in XSLT, for
example <xsl:stylesheet version="1.0" xmlns:x="{namespace-uri()}">
.
– Сергей Проскурнин yesterday
一个通用的解决方案,甚至不需要将 namespace-uri 作为参数传递:
在每个 XPath 表达式中替换(在 XSLT 代码中)
x:someElementName
和
*[local-name() = 'someElementName']
对您提供的代码执行此操作会导致此转换
请注意,现在不需要的带有前缀 x:
的命名空间声明已被删除!
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml" />
<xsl:template match="*[local-name()= 'Report']| *[local-name()= 'Table']| *[local-name()= 'tbl_MainGroup_Collection']">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*[local-name()= 'Report']/*[local-name()= 'Table']//*[local-name()= 'Data']">
<Data TxDev="{@TxDev}" DateTime="{@DateTime}" DBVersion="{@DBVersion}" ProductVersion="{@ProductVersion}" Version="{@Version}">
<xsl:apply-templates select="*[local-name()= 'tbl_SiteGroup_Collection']"/>
</Data>
</xsl:template>
<xsl:template match="*[local-name()= 'tbl_SiteGroup_Collection']">
<Sites>
<xsl:apply-templates select="*[local-name()= 'Site']"/>
</Sites>
</xsl:template>
<xsl:template match="*[local-name()= 'Site']">
<Site SiteNumber="{@SiteNumber}" SiteID="{@SiteID}" SiteName="{@SiteName}">
<xsl:apply-templates select="*[local-name()= 'tbl_ColumnGroup_Collection']"/>
</Site>
</xsl:template>
<xsl:template match="*[local-name()= 'tbl_ColumnGroup_Collection']">
<Columns>
<xsl:apply-templates select="*[local-name()= 'Column']"/>
</Columns>
</xsl:template>
<xsl:template match="*[local-name()='Column']">
<xsl:for-each select="@*[starts-with(local-name(), 'Column')]">
<Column ColumnNumber="{translate(local-name(),'Column','')}" ColumnName="{.}">
<xsl:variable name="attrName" select="local-name()"/>
<xsl:for-each select="../*[local-name()='Lines']">
<xsl:call-template name="Lines">
<xsl:with-param name="ColumnName" select="$attrName"/>
</xsl:call-template>
</xsl:for-each>
</Column>
</xsl:for-each>
</xsl:template>
<xsl:template name="Lines" match="*[local-name()='Lines']">
<xsl:param name="ColumnName"/>
<Lines>
<xsl:for-each select="*[local-name()='Line'][@LineNumber>0 and @EntityType=0]">
<xsl:sort select="@LineNumber" data-type="number" />
<xsl:call-template name="Line">
<xsl:with-param name="ColumnName" select="$ColumnName"/>
</xsl:call-template>
</xsl:for-each>
</Lines>
</xsl:template>
<xsl:template name="Line" match="*[local-name()='Line']">
<xsl:param name="ColumnName"/>
<Line LineNumber="{@LineNumber}" LineName="{@LineName}" >
<xsl:variable name="LineNo" select="@LineNumber"/>
<xsl:choose>
<xsl:when test="count(../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=1])>0">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
<Departments>
<xsl:for-each select="../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=1]">
<xsl:sort select="@EntityNo" data-type="number"/>
<Department DepNumber="{@EntityNo}" DepName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</Department>
</xsl:for-each>
</Departments>
</xsl:when>
<xsl:when test="count(../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=2])>0">
<xsl:if test="@*[local-name()=$ColumnName]">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:if>
<MOPs>
<xsl:for-each select="../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=2]">
<xsl:sort select="@EntityNo" data-type="number"/>
<MOP MOPNumber="{@EntityNo}" MOPName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</MOP>
</xsl:for-each>
</MOPs>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</Line>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
并将此转换应用于提供的源 XML 文档会产生与原始转换完全相同的输出。
警告:只有在保证引用的局部元素名称不与多个(不同的)命名空间一起使用时,此解决方案才是正确的。
我有 SSRS 2016 和一些报告(结构相似但名称和数据不同)要在 xml 导出期间进行转换。
报告xml示例:
<?xml version="1.0" encoding="utf-8"?>
<Report xsi:schemaLocation="My_Report_1 http://..." Name="My_Report_1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="My_Report_1">
<Table>
<tbl_MainGroup_Collection>
<Data TxDev="abc" DateTime="20200906001057" DBVersion="1.0" ProductVersion="1.0" Version="1.0">
<tbl_SiteGroup_Collection>
<Site SiteName="All sites">
<tbl_ColumnGroup_Collection>
<Column Column1="Week 33" Column2="Week 34">
<Lines>
<Line LineName="abc" Column1="0.00" Column2="0.00" LineNumber="1" EntityType="0" EntityNo="1" />
<Line LineName="dfg" LineNumber="-1" EntityType="0" EntityNo="2" />
</Lines>
</Column>
</tbl_ColumnGroup_Collection>
</Site>
</tbl_SiteGroup_Collection>
</Data>
</tbl_MainGroup_Collection>
</Table>
</Report>
XSLT 转换示例:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0"
xmlns:x="My_Report_1"
exclude-result-prefixes="x"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml" />
<xsl:template match="x:Report|x:Table|x:tbl_MainGroup_Collection">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="x:Report/x:Table//x:Data">
<Data TxDev="{@TxDev}" DateTime="{@DateTime}" DBVersion="{@DBVersion}" ProductVersion="{@ProductVersion}" Version="{@Version}">
<xsl:apply-templates select="x:tbl_SiteGroup_Collection"/>
</Data>
</xsl:template>
<xsl:template match="x:tbl_SiteGroup_Collection">
<Sites>
<xsl:apply-templates select="x:Site"/>
</Sites>
</xsl:template>
<xsl:template match="x:Site">
<Site SiteNumber="{@SiteNumber}" SiteID="{@SiteID}" SiteName="{@SiteName}">
<xsl:apply-templates select="x:tbl_ColumnGroup_Collection"/>
</Site>
</xsl:template>
<xsl:template match="x:tbl_ColumnGroup_Collection">
<Columns>
<xsl:apply-templates select="x:Column"/>
</Columns>
</xsl:template>
<xsl:template match="x:Column">
<xsl:for-each select="@*[starts-with(local-name(), 'Column')]">
<Column ColumnNumber="{translate(local-name(),'Column','')}" ColumnName="{.}">
<xsl:variable name="attrName" select="local-name()"/>
<xsl:for-each select="../x:Lines">
<xsl:call-template name="Lines">
<xsl:with-param name="ColumnName" select="$attrName"/>
</xsl:call-template>
</xsl:for-each>
</Column>
</xsl:for-each>
</xsl:template>
<xsl:template name="Lines" match="x:Lines">
<xsl:param name="ColumnName"/>
<Lines>
<xsl:for-each select="x:Line[@LineNumber>0 and @EntityType=0]">
<xsl:sort select="@LineNumber" data-type="number" />
<xsl:call-template name="Line">
<xsl:with-param name="ColumnName" select="$ColumnName"/>
</xsl:call-template>
</xsl:for-each>
</Lines>
</xsl:template>
<xsl:template name="Line" match="x:Line">
<xsl:param name="ColumnName"/>
<Line LineNumber="{@LineNumber}" LineName="{@LineName}" >
<xsl:variable name="LineNo" select="@LineNumber"/>
<xsl:choose>
<xsl:when test="count(../x:Line[@LineNumber=$LineNo and @EntityType=1])>0">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
<Departments>
<xsl:for-each select="../x:Line[@LineNumber=$LineNo and @EntityType=1]">
<xsl:sort select="@EntityNo" data-type="number"/>
<Department DepNumber="{@EntityNo}" DepName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</Department>
</xsl:for-each>
</Departments>
</xsl:when>
<xsl:when test="count(../x:Line[@LineNumber=$LineNo and @EntityType=2])>0">
<xsl:if test="@*[local-name()=$ColumnName]">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:if>
<MOPs>
<xsl:for-each select="../x:Line[@LineNumber=$LineNo and @EntityType=2]">
<xsl:sort select="@EntityNo" data-type="number"/>
<MOP MOPNumber="{@EntityNo}" MOPName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</MOP>
</xsl:for-each>
</MOPs>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</Line>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
由于命名空间绑定 xmlns:x="My_Report_1" 我只能将此 xslt 转换用于此报告。有没有什么方法可以生成更通用的 xslt 转换来处理所有此类报告,而不管 namespace-uri 的值如何?为命名空间值提供参数?
提前致谢。
来自 OP 的评论:
What I want is to pass some kind of variable or function in XSLT, for example
<xsl:stylesheet version="1.0" xmlns:x="{namespace-uri()}">
. – Сергей Проскурнин yesterday
一个通用的解决方案,甚至不需要将 namespace-uri 作为参数传递:
在每个 XPath 表达式中替换(在 XSLT 代码中)
x:someElementName
和
*[local-name() = 'someElementName']
对您提供的代码执行此操作会导致此转换
请注意,现在不需要的带有前缀 x:
的命名空间声明已被删除!
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes" method="xml" />
<xsl:template match="*[local-name()= 'Report']| *[local-name()= 'Table']| *[local-name()= 'tbl_MainGroup_Collection']">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*[local-name()= 'Report']/*[local-name()= 'Table']//*[local-name()= 'Data']">
<Data TxDev="{@TxDev}" DateTime="{@DateTime}" DBVersion="{@DBVersion}" ProductVersion="{@ProductVersion}" Version="{@Version}">
<xsl:apply-templates select="*[local-name()= 'tbl_SiteGroup_Collection']"/>
</Data>
</xsl:template>
<xsl:template match="*[local-name()= 'tbl_SiteGroup_Collection']">
<Sites>
<xsl:apply-templates select="*[local-name()= 'Site']"/>
</Sites>
</xsl:template>
<xsl:template match="*[local-name()= 'Site']">
<Site SiteNumber="{@SiteNumber}" SiteID="{@SiteID}" SiteName="{@SiteName}">
<xsl:apply-templates select="*[local-name()= 'tbl_ColumnGroup_Collection']"/>
</Site>
</xsl:template>
<xsl:template match="*[local-name()= 'tbl_ColumnGroup_Collection']">
<Columns>
<xsl:apply-templates select="*[local-name()= 'Column']"/>
</Columns>
</xsl:template>
<xsl:template match="*[local-name()='Column']">
<xsl:for-each select="@*[starts-with(local-name(), 'Column')]">
<Column ColumnNumber="{translate(local-name(),'Column','')}" ColumnName="{.}">
<xsl:variable name="attrName" select="local-name()"/>
<xsl:for-each select="../*[local-name()='Lines']">
<xsl:call-template name="Lines">
<xsl:with-param name="ColumnName" select="$attrName"/>
</xsl:call-template>
</xsl:for-each>
</Column>
</xsl:for-each>
</xsl:template>
<xsl:template name="Lines" match="*[local-name()='Lines']">
<xsl:param name="ColumnName"/>
<Lines>
<xsl:for-each select="*[local-name()='Line'][@LineNumber>0 and @EntityType=0]">
<xsl:sort select="@LineNumber" data-type="number" />
<xsl:call-template name="Line">
<xsl:with-param name="ColumnName" select="$ColumnName"/>
</xsl:call-template>
</xsl:for-each>
</Lines>
</xsl:template>
<xsl:template name="Line" match="*[local-name()='Line']">
<xsl:param name="ColumnName"/>
<Line LineNumber="{@LineNumber}" LineName="{@LineName}" >
<xsl:variable name="LineNo" select="@LineNumber"/>
<xsl:choose>
<xsl:when test="count(../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=1])>0">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
<Departments>
<xsl:for-each select="../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=1]">
<xsl:sort select="@EntityNo" data-type="number"/>
<Department DepNumber="{@EntityNo}" DepName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</Department>
</xsl:for-each>
</Departments>
</xsl:when>
<xsl:when test="count(../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=2])>0">
<xsl:if test="@*[local-name()=$ColumnName]">
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:if>
<MOPs>
<xsl:for-each select="../*[local-name()='Line'][@LineNumber=$LineNo and @EntityType=2]">
<xsl:sort select="@EntityNo" data-type="number"/>
<MOP MOPNumber="{@EntityNo}" MOPName="{@LineName}" Value="{@*[local-name()=$ColumnName]}">
</MOP>
</xsl:for-each>
</MOPs>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="Value">
<xsl:value-of select="@*[local-name()=$ColumnName]"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</Line>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
并将此转换应用于提供的源 XML 文档会产生与原始转换完全相同的输出。
警告:只有在保证引用的局部元素名称不与多个(不同的)命名空间一起使用时,此解决方案才是正确的。