5

我想用如下数据翻译一个 XML 文件:

<FlatData>
    <Details1_Collection>
        <Details1 Customer1="Customer" Total1="3" />
        ...
    </Details1_Collection>
</FlatData>

我感兴趣的数据是每个Details1. 问题是这些属性在我要翻译的每个 XML 文件中不一定都是相同的,我想要一个通用的 XSL 可以处理Details1如下:

<Details1 Customer1="Customer" Total1="3" />
<Details1 Name="Jim" Age="14" Weight="180" />
<Details1 Date="2009-07-27" Range="1-5" Option1="True" />

这些不同Details1不会出现在同一个源 XML 文件中,而是出现在不同的文件中。但是,我想在每个上使用相同的 XSL。

我在想我需要类似的东西,<xsl:value-of select="@attribute_name"/>但是@attribute_name当我事先不知道会有什么属性时,我该怎么做?另外,如何捕获属性名称?我想将上面的源 XML 分解为:

<Details1>
    <Customer1>Customer</Customer1>
    <Total1>3</Total1>
</Details1>

编辑:感谢您的回复!但是,我无法获得以下输出:

<?xml version="1.0" encoding="UTF-8"?>
<FlatData>
<Details1_Collection></Details1_Collection>
</FlatData>

我已经尝试了 lavinio 和 Jörn Horstmann 的答案,并尝试将两者结合起来。我运行这个命令:

msxsl.exe -o output.xml input.xml transform.xsl

我认为阻碍的是输入文件中的命名空间:

<Report Name="MyReport" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyReport">
4

6 回答 6

4

由于 Microsoft SQL Reporting Services 2008 命名空间是输入 XML 的一部分,因此难度增加了。起初我没有意识到这<Report Name="MyReport" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyReport">是一条如此重要的线。感谢 Pavel Minaev 的命名空间评论。以下 XSL 用于提取我想要的数据:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="EXQC005">
  <xsl:output method="xml" indent="yes" encoding="utf-8"/>

  <xsl:template match="/">
    <xsl:for-each select="a:Report/a:FlatData/a:Details1_Collection/a:Details1">
      <xsl:element name="{name(.)}">
        <xsl:for-each select="@*">
          <xsl:element name="{name(.)}">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:for-each>
      </xsl:element>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

我想我会尝试清理它以使用lavinio建议的apply-templates样式。还要感谢Jörn Horstmann的循环代码。弄清楚为什么 Reporting Services 报告最初转储时将值设置为报告名称而不是架构 URL会很有趣。select="@*"for-eachxmlns

在我完善这个 XSL 时,我将继续更新这个答案。

编辑: 这是一个与命名空间无关的版本,因为对于来自 Reporting Services 的每个不同报告,显然会有不同的命名空间:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" encoding="utf-8"/>

  <xsl:template match="/">
    <xsl:for-each select="*[local-name()='Report']/*[local-name()='FlatData']/*[local-name()='Details1_Collection']/*[local-name()='Details1']">
      <Details>
        <xsl:for-each select="@*">
          <xsl:element name="{name(.)}">
            <xsl:value-of select="."/>
          </xsl:element>
        </xsl:for-each>
      </Details>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
于 2009-07-28T17:13:26.423 回答
3

您可以使用"@*"来引用所有属性,例如以下示例:

  • <xsl:value-of select="@*"/>
  • <xsl:apply-templates select="@*"/>
  • <xsl:template match="@*">

<xsl:element name="">构造可用于创建具有任意名称的新元素,函数name()local-name()将返回特定属性的名称。

要做你想做的事,请尝试以下方式:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <FlatData>
            <Details1_Collection>
                <xsl:apply-templates select="FlatData/Details1_Collection/Details1"/>
            </Details1_Collection>
        </FlatData>
    </xsl:template>
    <xsl:template match="Details1">
        <Details1>
            <xsl:apply-templates select="@*"/>
        </Details1>
    </xsl:template>
    <xsl:template match="@*">
        <xsl:element name="{name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>
于 2009-07-27T21:33:16.663 回答
3

要解决命名空间问题(对于两个答案),请在您的 XLST中添加一个带有前缀的命名空间声明:

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

然后在所有 XPath 表达式中使用它来限定元素,例如:

<xsl:template match="//r:Details1">
于 2009-07-28T16:56:28.660 回答
2

这个转换是否给出了你想要的结果?

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

    <xsl:output method="xml" indent="yes" encoding="utf-8" />

    <xsl:template match="//Details1">
        <Details1>
            <xsl:for-each select="@*">
                <xsl:element name="{name(.)}"><xsl:value-of select="." /></xsl:element>
            </xsl:for-each>
        </Details1>
    </xsl:template>

</xsl:stylesheet>
于 2009-07-27T21:38:09.057 回答
0

编写 Jörn Horstmann 答案的另一种方法(如果您需要使用 Details1、Details2 等执行此操作......)将是:

<xsl:template match="//Details1 | //Details2 | //whatever">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="@*">
  <xsl:element name="{name(.)}">
    <xsl:value-of select="." />
  </xsl:element>
</xsl:template>
于 2009-07-27T21:45:52.457 回答
0

可能是最简单的方法:

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

  <xsl:template match="/">
    <FlatData>
      <xsl:copy-of select="//Details1" />
    </FlatData>
  </xsl:template>
</xsl:stylesheet>
于 2009-07-28T16:42:29.933 回答