基本上我是一个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 & Arthouse</xsl:when>
<xsl:when test=".= 'Japanese Cinema'">International > Japan</xsl:when>
<xsl:when test=".= 'Jidaigeki'">International > Japan</xsl:when>
<xsl:when test=".= 'Kids & 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 & 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 & 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>
我已经试着为此寻找一些解决方案,但我还没有找到解决方案,任何帮助都将不胜感激。
编辑澄清:我需要的是从输出中消除重复的流派元素。这些元素可以不相邻,我们不能通过第二次转换来运行输出,因为我们不能修改处理这一问题的服务的代码。
感谢
由于您似乎正在使用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 & Arthouse</xsl:when>
<xsl:when test=".= 'Japanese Cinema'">International > Japan</xsl:when>
<xsl:when test=".= 'Jidaigeki'">International > Japan</xsl:when>
<xsl:when test=".= 'Kids & 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 & 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 & 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>
注意:
IMHO,您对Xalan命名空间的声明:
xmlns:xalan="http://xml.apache.org/xslt"
不正确,应为:
xmlns:xalan="http://xml.apache.org/xalan"
一旦修复了这个问题,就可以使用Xalan扩展库中类似的函数:Xalan:distinct()和Xalan:nodeset()(并没有太大区别)。
应用此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="</xsl:text>
<xsl:value-of select="current()"/>
<xsl:text>" (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="</xsl:text>
<xsl:value-of select="$previous-if/@test"/>
<xsl:text>"</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文件来处理if
的测试值(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 > Klassiker</if>
<if test="Comedy">Komödie</if>
<if test="Concert Film">Musik</if>
<if test="Crime">Kriminalfilm > Drama</if>
<if test="Drama">Drama</if>
<if test="Fantasy">Drama > Sci-Fi und Fantasy</if>
<if test="Foreign">International</if>
<if test="Horror">Kriminalfilm > Horror</if>
<if test="Independent">Independentfilm & Arthouse</if>
<if test="Japanese Cinema">International > Japan</if>
<if test="Jidaigeki">International > Japan</if>
<if test="Kids & Family">Kinderfilm > Familie</if>
<if test="Music Documentary">Musik > Dokumentation</if>
<if test="Music Feature Film">Musik</if>
<if test="Musicals">Musik > Musical</if>
<if test="Mystery">Drama > Mystery</if>
<if test="Nonfiction - Documentary">Dokumentation</if>
<if test="Regional Indian">International > Indien & Pakistan</if>
<if test="Romance">Drama > Romanze</if>
<if test="Science Fiction">Science Fiction und Fantasy</if>
<if test="Short Films">Independentfilm & Arthouse > Experimentalfilm</if>
<if test="Special Interest">Hobby</if>
<if test="Sports">Sport</if>
<if test="Thrillers">Thriller</if>
<if test="Tokusatsu">International > Japan</if>
<if test="Urban">Drama > 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倍多的<genre/>
节点会使代码变长100倍)。
顺便说一句,有一个ifs.xml
来处理测试/值将使if比XSL中的原始编码测试更具可扩展性。