【问题标题】:Compare CSV in XSLT比较 XSLT 中的 CSV
【发布时间】:2012-01-18 07:53:04
【问题描述】:

这就是我想要实现的目标: 我有一个 CSV 文件,其中包含 1、4、5 等数据(不是固定系列),并且我有一个 XML,其中有某些节点重复。现在,我需要从该 XML 中删除其位置存在于 CSV 文件中的所有节点。

这就是我尝试的方式: 我将 CSV 文件作为参数传递给 XSLT 并调用递归模板来打印 XML。 (感谢很久以前看过的帖子。。不记得地址了)

问题:“这不起作用”:)

下面是我的示例 XML 和 XSLT。任何帮助将不胜感激。

XML:

<?xml version="1.0" encoding="UTF-8"?>
<Items>
    <Item IItemID="">
    </Item>
    <Item ItemID="100-8754">
    </Item>
    <Item ItemID="206-4141">
    </Item>
    <Item ItemID="">
    </Item>
</Items>

这是 XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes">
    <xsl:param name="ErrorPos" as="xs:string" select="'1,4'"/>
    <xsl:template match="*|/">
                <xsl:call-template name="commaSplit">
            <xsl:with-param name="dataString" select="$ErrorPos"/>
            <xsl:with-param name="position" select="1"/>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="commaSplit">
        <xsl:param name="dataString"/>
        <xsl:param name="position"/>
            Vivek
        <xsl:choose>
            <xsl:when test="contains($dataString,',')">
                <!-- Select the first value to process -->
                <xsl:call-template name="getItems">
                    <xsl:with-param name="errorPosition" select="substring-before($dataString,',')"/>
                    <xsl:with-param name="position" select="$position"/>
                </xsl:call-template>
                <!-- Recurse with remainder of string -->
                <xsl:call-template name="commaSplit">
                    <xsl:with-param name="dataString" select="substring-after($dataString,',')"/>
                    <xsl:with-param name="position" select="$position + 1"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <!-- This is the last value no need to  recurse -->
                <xsl:call-template name="getItems">
                    <xsl:with-param name="errorPosition" select="$dataString"/>
                    <xsl:with-param name="position" select="$position"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <!-- Process of individual value here -->
    <xsl:template name="getItems" match="/">
        <xsl:param name="errorPosition"/>
        <xsl:param name="position"/>
        <Items>
            <xsl:value-of select="$errorPosition"/>
            <xsl:value-of select="concat(',',$position)"/><!--Just for testing...will be replaced by copy statement-->
        </Items>
    </xsl:template>

</xsl:stylesheet>

【问题讨论】:

  • 为什么选择 XSLT?正如您所发现的,XSLT 不了解 CSV,这使得使用 CSV 做任何事情都成为绝对的麻烦。因此,通过 C#/Java/{language of chioce} 中的 DOM 操作更容易做到这一点。
  • 不,克里斯...实际上我们没有使用 C#..但是我对 JAVA 不太满意,所以如果您能提出任何相同的建议,我会很乐意浏览。跨度>
  • 这就是为什么我将 {language of choice} 添加到列表中的原因,因为这些天几乎每个环境都有一个 XML DOM 解析器,这就是为什么我问你为什么要限制自己使用 XSLT。跨度>
  • 所以您想获得
  • @ChrisJ:您似乎不了解 XSLT 2.0。 5 到 6 年前,人们认为使用 DOM 可能是查询/处理 XML 的众多替代方案中最糟糕的选择。

标签: xml xslt csv xslt-2.0


【解决方案1】:

您的问题是因为您有 2 个模板都匹配“/” xml 文档的根目录,为了正确汇集数据,您必须确保在您希望首先执行的模板上设置优先级!由于您使用的是命名模板,因此您可以简单地从 getItems 模板中删除 match 属性!

有了这个微小的改变,它就可以工作,除非你需要不同的输出?

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes">
    <xsl:param name="ErrorPos" as="xs:string" select="'1,4'"/>
    <xsl:template match="*|/" priority="999">
                <xsl:call-template name="commaSplit">
            <xsl:with-param name="dataString" select="$ErrorPos"/>
            <xsl:with-param name="position" select="1"/>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="commaSplit">
        <xsl:param name="dataString"/>
        <xsl:param name="position"/>
            Vivek
        <xsl:choose>
            <xsl:when test="contains($dataString,',')">
                <!-- Select the first value to process -->
                <xsl:call-template name="getItems">
                    <xsl:with-param name="errorPosition" select="substring-before($dataString,',')"/>
                    <xsl:with-param name="position" select="$position"/>
                </xsl:call-template>
                <!-- Recurse with remainder of string -->
                <xsl:call-template name="commaSplit">
                    <xsl:with-param name="dataString" select="substring-after($dataString,',')"/>
                    <xsl:with-param name="position" select="$position + 1"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <!-- This is the last value no need to  recurse -->
                <xsl:call-template name="getItems">
                    <xsl:with-param name="errorPosition" select="$dataString"/>
                    <xsl:with-param name="position" select="$position"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <!-- Process of individual value here -->
    <xsl:template name="getItems" match="/">
        <xsl:param name="errorPosition"/>
        <xsl:param name="position"/>
        <Items>
            <xsl:value-of select="$errorPosition"/>
            <xsl:value-of select="concat(',',$position)"/><!--Just for testing...will be replaced by copy statement-->
        </Items>
    </xsl:template>

</xsl:stylesheet>

问候, 山姆

【讨论】:

  • @Vivek,TreeMonkey:为什么不真正使用 XSLT 2.0 作为它的预期语言?将它用作普通的 XSLT 1.0 是一种耻辱。
  • @DimitreNovaatchev 我只知道 xslt 1.0 在 .net 环境中工作,所以这种情况很快发生变化的可能性很小,尽管我从你的回答中看到,就我的目的而言,它可能非常方便!
  • @Treemonkey:但是 OP 的代码和 your 答案都在 XSLT 2.0 中??!?如果是这样,那么我们应该编写一个真正的 XSLT 2.0 代码,而不是一个看起来不像 XSLT 2.0 并且当读者看到和重用它们时是在给他们做坏事的代码。
【解决方案2】:

好问题,+1。

请注意,您使用的是 XSLT 2.0,但根本没有使用任何 XSLT 2.0 特定的功能。

这里有一个简短的(单一模板,没有递归,没有xsl:call-template,没有xsl:choosexsl:whenxsl:otherwisetrue XSLT 2.0 解决方案

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

 <xsl:param name="pErrorPos" as="xs:string" select="'1,4'"/>

 <xsl:variable name="vDeletePos" as="xs:integer*" select=
 " for $s in tokenize($pErrorPos, ',')
    return xs:integer($s)
 "/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="Item[position() = $vDeletePos]"/>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Items>
    <Item IItemID=""/>
    <Item ItemID="100-8754"/>
    <Item ItemID="206-4141"/>
    <Item ItemID=""/>
</Items>

产生想要的正确结果

<Items>
   <Item ItemID="100-8754"/>
   <Item ItemID="206-4141"/>
</Items>

解释

  1. 覆盖 Identity rule

  2. 使用标准 XPath 函数 tokenize()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    相关资源
    最近更新 更多