【问题标题】:XLST 1.0 Group elements in output after changing valuesXSLT 1.0在更改值后对输出中的元素进行分组
【发布时间】:2014-11-16 18:49:09
【问题描述】:

基本上我是 xlst 新手,负责对大型 xls 文件进行一些更改,该文件处理德国市场的电影元数据转换。

xls 文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:str="http://exslt.org/strings" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:redirect="http://xml.apache.org/xalan/redirect" extension-element-prefixes="redirect" xmlns:xalan="http://xml.apache.org/xslt" exclude-result-prefixes="xalan str">
<xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/metadata">
    <xsl:variable name="featureID" select="substring(mpm_product_id, 7, string-length(mpm_product_id))"/>
    <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
    <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
    <Metadata>

    <some values...>

    <xsl:for-each select="genres/genre">

                    <Genre>
                            <xsl:choose>

                              <!-- Mappings for German Genres -->
                                <xsl:when test="/metadata/base/territory_code='DE'">
                                    <xsl:choose>

                                        <xsl:when test=".= 'Action'">Action und Abenteuer</xsl:when>
                                        <xsl:when test=".= 'Adventure'">Action und Abenteuer</xsl:when>
                                        <xsl:when test=".= 'Animation'">Zeichentrick</xsl:when>
                                        <xsl:when test=".= 'Anime'">Zeichentrick</xsl:when>
                                        <xsl:when test=".= 'Bollywood'">Bollywood</xsl:when>
                                        <xsl:when test=".= 'Classics'">Drama > Klassiker</xsl:when>
                                        <xsl:when test=".= 'Comedy'">Komödie</xsl:when>
                                        <xsl:when test=".= 'Concert Film'">Musik</xsl:when>
                                        <xsl:when test=".= 'Crime'">Kriminalfilm > Drama</xsl:when>
                                        <xsl:when test=".= 'Drama'">Drama</xsl:when>
                                        <xsl:when test=".= 'Fantasy'">Drama > Sci-Fi und Fantasy</xsl:when>
                                        <xsl:when test=".= 'Foreign'">International</xsl:when>
                                        <xsl:when test=".= 'Horror'">Kriminalfilm > Horror</xsl:when>
                                        <xsl:when test=".= 'Independent'">Independentfilm &amp; Arthouse</xsl:when>
                                        <xsl:when test=".= 'Japanese Cinema'">International > Japan</xsl:when>
                                        <xsl:when test=".= 'Jidaigeki'">International > Japan</xsl:when>
                                        <xsl:when test=".= 'Kids &amp; Family'">Kinderfilm > Familie</xsl:when>
                                        <xsl:when test=".= 'Music Documentary'">Musik > Dokumentation</xsl:when>
                                        <xsl:when test=".= 'Music Feature Film'">Musik</xsl:when>
                                        <xsl:when test=".= 'Musicals'">Musik > Musical</xsl:when>
                                        <xsl:when test=".= 'Mystery'">Drama > Mystery</xsl:when>
                                        <xsl:when test=".= 'Nonfiction - Documentary'">Dokumentation</xsl:when>
                                        <xsl:when test=".= 'Regional Indian'">International > Indien &amp; Pakistan</xsl:when>
                                        <xsl:when test=".= 'Romance'">Drama > Romanze</xsl:when>
                                        <xsl:when test=".= 'Science Fiction'">Science Fiction und Fantasy</xsl:when>
                                        <xsl:when test=".= 'Short Films'">Independentfilm &amp; Arthouse > Experimentalfilm</xsl:when>
                                        <xsl:when test=".= 'Special Interest'">Hobby</xsl:when>
                                        <xsl:when test=".= 'Sports'">Sport</xsl:when>
                                        <xsl:when test=".= 'Thrillers'">Thriller</xsl:when>
                                        <xsl:when test=".= 'Tokusatsu'">International > Japan</xsl:when>
                                        <xsl:when test=".= 'Urban'">Drama > Alltag</xsl:when>
                                        <xsl:when test=".= 'Westerns'">Western</xsl:when>

                                        <xsl:otherwise>
                                            <xsl:value-of select="." />
                                        </xsl:otherwise>
                                     </xsl:choose>
                                </xsl:when>


                            </xsl:choose> 

                    </Genre>
                </xsl:for-each>
       <More values...>
    </Metadata>
</xsl:template>

问题是当类型转换时我们以重复值结尾,例如当输入包含元素时

<genres>
    <genre>Comedy</genre>
    <genre>Adventure</genre>
    <genre>Action</genre>
</genres>

转换后我们有

<Genre>Komödie</Genre>
<Genre>Action und Abenteuer</Genre>
<Genre>Action und Abenteuer</Genre>

我已经尝试为此寻找一些解决方案,但我还没有找到解决方案,我们将不胜感激。

编辑澄清:我需要的是从输出中消除重复的流派元素。这些元素不能彼此相邻,我们无法通过第二次转换运行输出,因为我们无法修改处理此问题的服务代码。

谢谢

【问题讨论】:

  • 好吧,您至少需要准确地告诉我们您想要的结果。您想消除重复的genre 元素吗?您是否只想在它们相邻的情况下(例如在您的样本中)或即使它们分开?您是否考虑过链接两个样式表,您只需将当前样式表的输出运行一秒钟即可通过 Muenchian 分组 jenitennison.com/xslt/grouping/muenchian.xml 消除重复?
  • 理想情况下,重复的流派元素需要从列表中删除,它们并不总是彼此相邻。此外,它不可能通过第二个样式表运行输出。我将用这些说明更新问题。
  • 您需要另一个包含 if 条目值的 XML,因此您可以使用 XPath 检查 preceding-sibling 节点。

标签: xml xslt xslt-1.0


【解决方案1】:

由于您似乎使用 Xalan 作为 XSLT 处理器,您可以通过使用该处理器支持的几个扩展功能轻松解决此问题。这是一个使用 EXSLT 的示例(从 xmlns:str 命名空间声明来看,您似乎已经在使用它):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

xmlns:set="http://exslt.org/sets"
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings" 

xmlns:xalan="http://xml.apache.org/xslt" 
xmlns:redirect="http://xml.apache.org/xalan/redirect" 

exclude-result-prefixes="set exsl str xalan"
extension-element-prefixes="redirect">

<xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/metadata">
    <xsl:variable name="featureID" select="substring(mpm_product_id, 7, string-length(mpm_product_id))"/>
    <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
    <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
    <Metadata>
        <xsl:variable name="genres">
            <xsl:for-each select="genres/genre">
                <Genre>
                    <xsl:choose>
                        <xsl:when test="/metadata/base/territory_code='DE'">
                            <xsl:choose>
                                <xsl:when test=".= 'Action'">Action und Abenteuer</xsl:when>
                                <xsl:when test=".= 'Adventure'">Action und Abenteuer</xsl:when>
                                <xsl:when test=".= 'Animation'">Zeichentrick</xsl:when>
                                <xsl:when test=".= 'Anime'">Zeichentrick</xsl:when>
                                <xsl:when test=".= 'Bollywood'">Bollywood</xsl:when>
                                <xsl:when test=".= 'Classics'">Drama > Klassiker</xsl:when>
                                <xsl:when test=".= 'Comedy'">Komödie</xsl:when>
                                <xsl:when test=".= 'Concert Film'">Musik</xsl:when>
                                <xsl:when test=".= 'Crime'">Kriminalfilm > Drama</xsl:when>
                                <xsl:when test=".= 'Drama'">Drama</xsl:when>
                                <xsl:when test=".= 'Fantasy'">Drama > Sci-Fi und Fantasy</xsl:when>
                                <xsl:when test=".= 'Foreign'">International</xsl:when>
                                <xsl:when test=".= 'Horror'">Kriminalfilm > Horror</xsl:when>
                                <xsl:when test=".= 'Independent'">Independentfilm &amp; Arthouse</xsl:when>
                                <xsl:when test=".= 'Japanese Cinema'">International > Japan</xsl:when>
                                <xsl:when test=".= 'Jidaigeki'">International > Japan</xsl:when>
                                <xsl:when test=".= 'Kids &amp; Family'">Kinderfilm > Familie</xsl:when>
                                <xsl:when test=".= 'Music Documentary'">Musik > Dokumentation</xsl:when>
                                <xsl:when test=".= 'Music Feature Film'">Musik</xsl:when>
                                <xsl:when test=".= 'Musicals'">Musik > Musical</xsl:when>
                                <xsl:when test=".= 'Mystery'">Drama > Mystery</xsl:when>
                                <xsl:when test=".= 'Nonfiction - Documentary'">Dokumentation</xsl:when>
                                <xsl:when test=".= 'Regional Indian'">International > Indien &amp; Pakistan</xsl:when>
                                <xsl:when test=".= 'Romance'">Drama > Romanze</xsl:when>
                                <xsl:when test=".= 'Science Fiction'">Science Fiction und Fantasy</xsl:when>
                                <xsl:when test=".= 'Short Films'">Independentfilm &amp; Arthouse > Experimentalfilm</xsl:when>
                                <xsl:when test=".= 'Special Interest'">Hobby</xsl:when>
                                <xsl:when test=".= 'Sports'">Sport</xsl:when>
                                <xsl:when test=".= 'Thrillers'">Thriller</xsl:when>
                                <xsl:when test=".= 'Tokusatsu'">International > Japan</xsl:when>
                                <xsl:when test=".= 'Urban'">Drama > Alltag</xsl:when>
                                <xsl:when test=".= 'Westerns'">Western</xsl:when>
                            <xsl:otherwise>
                                <xsl:value-of select="." />
                            </xsl:otherwise>
                         </xsl:choose>
                        </xsl:when>
                    </xsl:choose> 
                </Genre>
            </xsl:for-each> 
        </xsl:variable>

        <xsl:copy-of select="set:distinct(exsl:node-set($genres)/Genre)"/>

    </Metadata>

</xsl:template>

</xsl:stylesheet>

应用于您的输入示例,结果为:

<?xml version="1.0" encoding="UTF-8"?>
<Metadata>
    <Genre>Komödie</Genre>
    <Genre>Action und Abenteuer</Genre>
    <Genre>B-Grade</Genre>
</Metadata>

注意:

恕我直言,您对 Xalan 命名空间的声明:

xmlns:xalan="http://xml.apache.org/xslt" 

不正确,应改为:

xmlns:xalan="http://xml.apache.org/xalan" 

一旦你解决了这个问题,你就可以使用 Xalan 扩展库中的类似函数:xalan:distinct() 和 xalan:nodeset()(并不是说它有很大的不同)。

【讨论】:

  • 我试过了,但它的转换没有返回任何元素
  • @MiguelArriaga 在处理代码 sn-ps 时很难说出问题所在。这对你有用吗? xsltransform.net/jyH9rM2
  • 感谢迈克尔的快速回复。实际上我得到了它的工作与你的建议。谢谢
【解决方案2】:

应用此 XSLT (style.xsl)

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

<xsl:stylesheet
    xmlns:str="http://exslt.org/strings"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:redirect="http://xml.apache.org/xalan/redirect"
    extension-element-prefixes="redirect"
    xmlns:xalan="http://xml.apache.org/xslt"
    exclude-result-prefixes="xalan str">

    <xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>

    <xsl:strip-space elements="*"/>

    <xsl:template match="/metadata">
        <xsl:variable name="featureID" select="substring(mpm_product_id, 7, string-length(mpm_product_id))"/>
        <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
        <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />

        <Metadata>
            <xsl:if test="/metadata/base/territory_code='DE'">
                <!-- Applying template is the way XSLT works -->
                <xsl:apply-templates select="genres/genre">
                    <xsl:with-param name="ifs" select="document('ifs.xml')/ifs"/>
                </xsl:apply-templates>
            </xsl:if>
        </Metadata>
    </xsl:template>

    <xsl:template match="genre">
        <xsl:param name="ifs"/>

        <!-- if if/@test=current() then we'll display if/text() -->
        <xsl:variable name="this-if"        select="$ifs/if[@test=current()]"/>
        <!-- gets all 'ifs' using previous 'genre' that have same value as $this-if -->
        <xsl:variable name="previous-if"    select="$ifs/if[string(.)=string($this-if) and @test=current()/preceding-sibling::genre]"/>

        <Genre>
            <xsl:choose>
                <xsl:when test="$previous-if"> <!-- Duplicate -->
                    <xsl:text>Node genre=&quot;</xsl:text>
                    <xsl:value-of select="current()"/>
                    <xsl:text>&quot; (if=</xsl:text>
                    <xsl:value-of select="$this-if"/>
                    <xsl:text>; id:</xsl:text>
                    <xsl:value-of select="generate-id()"/>
                    <xsl:text> is a duplicate of if/@test=&quot;</xsl:text>
                    <xsl:value-of select="$previous-if/@test"/>
                    <xsl:text>&quot;</xsl:text>
                </xsl:when>
                <!-- Not duplicate + there is a 'if' entry -->
                <xsl:when test="$this-if">
                    <xsl:apply-templates select="$this-if"/>
                </xsl:when>
                <!-- Not duplicate + there is no 'if' entry -->
                <xsl:otherwise>
                    <xsl:value-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </Genre>
    </xsl:template>

    <xsl:template match="if">
        <xsl:value-of select="."/>
    </xsl:template>
</xsl:stylesheet>

致此来源 (source.xml)

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="style.xsl"?>

<metadata>
    <base>
        <territory_code>DE</territory_code>
    </base>
    <genres>
        <genre>Comedy</genre>
        <genre>Adventure</genre>
        <genre>Action</genre>
        <genre>B-Grade</genre>
    </genres>
</metadata>

并使用新的 XML 文件来处理 ifs 测试值 (ifs.xml):

<?xml version="1.0" encoding="utf-8"?>

<ifs>
    <if test="Action">Action und Abenteuer</if>
    <if test="Adventure">Action und Abenteuer</if>
    <if test="Animation">Zeichentrick</if>
    <if test="Anime">Zeichentrick</if>
    <if test="Bollywood">Bollywood</if>
    <if test="Classics">Drama &gt; Klassiker</if>
    <if test="Comedy">Komödie</if>
    <if test="Concert Film">Musik</if>
    <if test="Crime">Kriminalfilm &gt; Drama</if>
    <if test="Drama">Drama</if>
    <if test="Fantasy">Drama &gt; Sci-Fi und Fantasy</if>
    <if test="Foreign">International</if>
    <if test="Horror">Kriminalfilm &gt; Horror</if>
    <if test="Independent">Independentfilm &amp; Arthouse</if>
    <if test="Japanese Cinema">International &gt; Japan</if>
    <if test="Jidaigeki">International &gt; Japan</if>
    <if test="Kids &amp; Family">Kinderfilm &gt; Familie</if>
    <if test="Music Documentary">Musik &gt; Dokumentation</if>
    <if test="Music Feature Film">Musik</if>
    <if test="Musicals">Musik &gt; Musical</if>
    <if test="Mystery">Drama &gt; Mystery</if>
    <if test="Nonfiction - Documentary">Dokumentation</if>
    <if test="Regional Indian">International &gt; Indien &amp; Pakistan</if>
    <if test="Romance">Drama &gt; Romanze</if>
    <if test="Science Fiction">Science Fiction und Fantasy</if>
    <if test="Short Films">Independentfilm &amp; Arthouse &gt; Experimentalfilm</if>
    <if test="Special Interest">Hobby</if>
    <if test="Sports">Sport</if>
    <if test="Thrillers">Thriller</if>
    <if test="Tokusatsu">International &gt; Japan</if>
    <if test="Urban">Drama &gt; Alltag</if>
    <if test="Westerns">Western</if>
</ifs>

你得到

<Metadata>
    <Genre>Komödie</Genre>
    <Genre>Action und Abenteuer</Genre>
    <Genre>Node genre="Action" (if=Action und Abenteuer; id:id0xe2bc950 is a duplicate of if/@test="Adventure"</Genre>
    <Genre>B-Grade</Genre>
</Metadata>

因此,您可以对找到的重复项做任何您想做的事情。请注意,对每个 genre 节点使用 preceding-sibling:: 会生成算法 O(N²)(也就是 10 倍以上的 &lt;genre/&gt; 节点将使代码长 100 倍)。

顺便说一句,拥有ifs.xml 来处理测试/值将比 XSL 中的原始编码测试更具可扩展性。

【讨论】:

  • 我建议您熟悉 Muenchian 分组 - 请参阅上面 Martin Honnen 评论中的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
相关资源
最近更新 更多