【问题标题】:Excluding XML empty nodes with XSLT使用 XSLT 排除 XML 空节点
【发布时间】:2012-06-09 02:42:53
【问题描述】:

我有一些非常简单的 XML。我正在重新创建一些 XML 并将一些样板文本添加到最终的 XML 文件中,以便在完成后导入 InDesign。

问题是:并非所有的 XML 字段都被用于每条记录。因此,当 XSLT 添加样板文本时,它甚至出现在不包含 XML 元素的记录中。

我尝试使用选择 >> when >> else 来查找元素,如果存在则使用该元素,或者如果该元素不在记录中,则忽略样板并插入 NOTHING。

这里是一些示例 XML 数据:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <story>
        <CL>
            <CityDescription>City One</CityDescription>
            <BK>
                <CompanyName>Corporate Name</CompanyName>
                <address>123 Main St</address>
                <HoldingCo>Company Name</HoldingCo>
                <TotalAssets>128,319,000</TotalAssets>
                <TotalLiabilities>117,059,000</TotalLiabilities>
                <TotalDeposits>89,847,000</TotalDeposits>
                <EquityCapital>11,260,000</EquityCapital>
            </BK>
            <BK>
                <CompanyName>Smaller Company</CompanyName>
                <address>123 Central St</address>
            </BK>
        </CL>
        <CL>
            <CityDescription>City Two</CityDescription>
            <BK>
                <CompanyName>Corporate Name Three</CompanyName>
                <address>123 High St</address>
                <HoldingCo>Company Name</HoldingCo>
                <TotalAssets>128,319,000</TotalAssets>
                <TotalLiabilities>117,059,000</TotalLiabilities>
                <TotalDeposits>89,847,000</TotalDeposits>
                <EquityCapital>11,260,000</EquityCapital>
            </BK>
            <BK>
                <CompanyName>Smaller Company Four</CompanyName>
                <address>123 Jones St</address>
            </BK>
        </CL>
    </story>
</root>

这是我尝试使用的 XSLT,但它甚至在不包含元素的记录中添加了“Holding Co:”和“Total Assets:”:

<?xml version="1.0" encoding="UTF-8"?><!-- DWXMLSource="Testing.xml" -->
<!DOCTYPE xsl:stylesheet  [
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="XML" />
<xsl:template match="/">

<root>
    <story>
        <xsl:for-each select="root/story/CL">
            <CityDescription><xsl:value-of select="CityDescription"/></CityDescription><xsl:text>
            </xsl:text>
            <xsl:for-each select="BK">
                <CompanyName><xsl:value-of select="CompanyName"/></CompanyName><xsl:text>
                </xsl:text>
                <address><xsl:value-of select="address"/></address><xsl:text>
                </xsl:text>
                <HoldingCo><xsl:text>Holding Co: </xsl:text><xsl:value-of select="HoldingCo"/></HoldingCo><xsl:text>
                </xsl:text>
                <TotalAssets><xsl:text>Total Assets: </xsl:text><xsl:value-of select="TotalAssets"/></TotalAssets><xsl:text>
                </xsl:text>
                <TotalLiabilities><xsl:text>Total Liabilities: </xsl:text><xsl:value-of select="TotalLiabilities"/></TotalLiabilities><xsl:text>
                </xsl:text>
                <TotalDeposits><xsl:text>Total Deposits: </xsl:text><xsl:value-of select="TotalDeposits"/></TotalDeposits><xsl:text>
                </xsl:text>
                <EquityCapital><xsl:text>Total Assets: </xsl:text><xsl:value-of select="EquityCapital"/></EquityCapital><xsl:text>
                </xsl:text>
            </xsl:for-each>
        </xsl:for-each>
    </story>
</root>
</xsl:template>
</xsl:stylesheet>

我尝试使用选择>>当>>否则基本上忽略元素,当它们没有出现在数据中时,但我的输出只显示“否则”内容。

有什么建议吗?

【问题讨论】:

  • 学习在样式表中使用模板规则,而不是使用单一的 for-each。然后很容易细化规则以涵盖特殊情况,例如元素为空。

标签: xml xslt


【解决方案1】:
Here's the problem: not all XML fields are being used in every record. So, when the XSLT adds the boilerplate text it appears even in the records that don't include the XML elements.

这就是模板的用途——在 XSLT 中不使用模板就像在 OO 编程语言中不使用类

这个简单的转换(注意没有使用一条条件指令)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes" />
    <xsl:strip-space elements="*"/>

    <xsl:variable name="vNL" select="'&#xA;'"/>
    <xsl:template match="/">
        <root>
            <story>
              <xsl:apply-templates select="root/story/CL"/>
            </story>
        </root>
    </xsl:template>

    <xsl:template match="CL">
      <xsl:apply-templates select="CityDescription"/>
      <xsl:apply-templates select="BK"/>
    </xsl:template>

    <xsl:template match="CityDescription | CompanyName | address">
        <xsl:element name="{name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="BK">
      <xsl:value-of select="$vNL"/>
      <xsl:apply-templates select="CompanyName"/>
      <xsl:apply-templates select="address"/>
      <xsl:apply-templates select="HoldingCo"/>
      <xsl:apply-templates select="TotalAssets"/>
      <xsl:apply-templates select="TotalLiabilities"/>
      <xsl:apply-templates select="TotalDeposits"/>
      <xsl:apply-templates select="EquityCapital"/>
    </xsl:template>

    <xsl:template match="HoldingCo">
        <HoldingCo>
            <xsl:text>Holding Co: </xsl:text>
            <xsl:value-of select="."/>
        </HoldingCo>
    </xsl:template>

    <xsl:template match="TotalAssets">
        <TotalAssets>
            <xsl:text>Total Assets: </xsl:text>
            <xsl:value-of select="."/>
        </TotalAssets>
    </xsl:template>

    <xsl:template match="TotalLiabilities">
        <TotalLiabilities>
            <xsl:text>Total Liabilities: </xsl:text>
            <xsl:value-of select="."/>
        </TotalLiabilities>
    </xsl:template>

    <xsl:template match="TotalDeposits">
        <TotalDeposits>
            <xsl:text>Total Deposits: </xsl:text>
            <xsl:value-of select="."/>
        </TotalDeposits>
    </xsl:template>

    <xsl:template match="EquityCapital">
        <EquityCapital>
            <xsl:text>Total Assets: </xsl:text>
            <xsl:value-of select="."/>
        </EquityCapital>
    </xsl:template>

    <xsl:template match="text()"/>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<root>
    <story>
        <CL>
            <CityDescription>City One</CityDescription>
            <BK>
                <CompanyName>Corporate Name</CompanyName>
                <address>123 Main St</address>
                <HoldingCo>Company Name</HoldingCo>
                <TotalAssets>128,319,000</TotalAssets>
                <TotalLiabilities>117,059,000</TotalLiabilities>
                <TotalDeposits>89,847,000</TotalDeposits>
                <EquityCapital>11,260,000</EquityCapital>
            </BK>
            <BK>
                <CompanyName>Smaller Company</CompanyName>
                <address>123 Central St</address>
            </BK>
        </CL>
        <CL>
            <CityDescription>City Two</CityDescription>
            <BK>
                <CompanyName>Corporate Name Three</CompanyName>
                <address>123 High St</address>
                <HoldingCo>Company Name</HoldingCo>
                <TotalAssets>128,319,000</TotalAssets>
                <TotalLiabilities>117,059,000</TotalLiabilities>
                <TotalDeposits>89,847,000</TotalDeposits>
                <EquityCapital>11,260,000</EquityCapital>
            </BK>
            <BK>
                <CompanyName>Smaller Company Four</CompanyName>
                <address>123 Jones St</address>
            </BK>
        </CL>
    </story>
</root>

产生想要的正确结果:

<root>
   <story>
      <CityDescription>City One</CityDescription>

      <CompanyName>Corporate Name</CompanyName>
      <address>123 Main St</address>
      <HoldingCo>Holding Co: Company Name</HoldingCo>
      <TotalAssets>Total Assets: 128,319,000</TotalAssets>
      <TotalLiabilities>Total Liabilities: 117,059,000</TotalLiabilities>
      <TotalDeposits>Total Deposits: 89,847,000</TotalDeposits>
      <EquityCapital>Total Assets: 11,260,000</EquityCapital>

      <CompanyName>Smaller Company</CompanyName>
      <address>123 Central St</address>
      <CityDescription>City Two</CityDescription>

      <CompanyName>Corporate Name Three</CompanyName>
      <address>123 High St</address>
      <HoldingCo>Holding Co: Company Name</HoldingCo>
      <TotalAssets>Total Assets: 128,319,000</TotalAssets>
      <TotalLiabilities>Total Liabilities: 117,059,000</TotalLiabilities>
      <TotalDeposits>Total Deposits: 89,847,000</TotalDeposits>
      <EquityCapital>Total Assets: 11,260,000</EquityCapital>

      <CompanyName>Smaller Company Four</CompanyName>
      <address>123 Jones St</address>
   </story>
</root>

由于BK的子元素是按文档顺序处理的,匹配模板可以简化为:

<xsl:template match="BK">
    <xsl:value-of select="$vNL"/>
    <xsl:apply-templates/>
</xsl:template>

同样适用于匹配CL的模板——可以替换为:

<xsl:template match="CL">
    <xsl:apply-templates/>
</xsl:template>

最后,这个模板可以完全删除,因为它完全复制了匹配任何元素的 XSLT内置模板

因此,这些重构之后的转换是

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes" />
    <xsl:strip-space elements="*"/>

    <xsl:variable name="vNL" select="'&#xA;'"/>
    <xsl:template match="/">
        <root>
            <story>
              <xsl:apply-templates select="root/story/CL"/>
            </story>
        </root>
    </xsl:template>

    <xsl:template match="CityDescription | CompanyName | address">
        <xsl:element name="{name()}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="BK">
        <xsl:value-of select="$vNL"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="HoldingCo">
        <HoldingCo>
            <xsl:text>Holding Co: </xsl:text>
            <xsl:value-of select="."/>
        </HoldingCo>
    </xsl:template>

    <xsl:template match="TotalAssets">
        <TotalAssets>
            <xsl:text>Total Assets: </xsl:text>
            <xsl:value-of select="."/>
        </TotalAssets>
    </xsl:template>

    <xsl:template match="TotalLiabilities">
        <TotalLiabilities>
            <xsl:text>Total Liabilities: </xsl:text>
            <xsl:value-of select="."/>
        </TotalLiabilities>
    </xsl:template>

    <xsl:template match="TotalDeposits">
        <TotalDeposits>
            <xsl:text>Total Deposits: </xsl:text>
            <xsl:value-of select="."/>
        </TotalDeposits>
    </xsl:template>

    <xsl:template match="EquityCapital">
        <EquityCapital>
            <xsl:text>Total Assets: </xsl:text>
            <xsl:value-of select="."/>
        </EquityCapital>
    </xsl:template>

    <xsl:template match="text()"/>
</xsl:stylesheet>

解释

指令:

<xsl:apply-templates select="someChildName"/>

仅当存在someChildName 子节点时才应用模板(执行处理)。

【讨论】:

  • @Demitre 感谢您的回答。我认为这是问题所在,但我没有为这种类型的 XML 使用模板。 CHOOSE >> WHEN >> OTHERWISE 会起作用吗?或者,是否需要在每个节点中使用相同的 XML?
  • @Demitre。我不熟悉您在此处创建的变量名称: 这些是随机名称吗?还是它们有意义?看起来 select 是一个实体...
  • @JimMaivald: [Re: 会选择 >> WHEN >> OTHERWISE 工作吗?] 如果你问 C/C#/C++/Java 等,答案是一样的:“@987654332 @ 工作吗?”
  • @JimMaivald:我使用名称 $vNL 来表示(并具有值)“换行符”。您可能已经看到分别表示“新行”和“回车”的缩写 NL 和 CR。我还将定义的每个变量名称都以字符 v 开头,以避免在我错过变量引用的起始 $ 字符并且在源 XML 文档中恰好有一个具有相同名称的元素时出现错误——这可能会导致 XSLT 处理器问题很难检测到且未报告。
  • 感谢您的帮助。最后一个问题:这个当前的 XML 没有空元素,只是没有导出元素。但是,如果 XML 包含 之类的元素,您的建议是否仍然有效
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-18
相关资源
最近更新 更多