我遇到了以下XSLT 2.0处理问题: 如何获取元素(在下一个示例输入中命名为'type')仅报告完整信息一次,然后仅报告参考(href)?
<top-level>
<group id="1">
<object id="objectA">
<property>
<value>Jack</value>
</property>
<type-tref>/types/type1</type-tref>
</object>
<object id="objectB">
<property>
<value>Jim</value>
</property>
<type-tref>/types/type2</type-tref>
</object>
<object id="objectC">
<property>
<value>John</value>
</property>
<type-tref>/types/type1</type-tref>
</object>
</group>
<group id="2">
<object id="objectD">
<property>
<value>Jill</value>
</property>
<type-tref>/types/type1</type-tref>
</object>
</group>
<specialObjects>
<object id="objectE">
<property>
<value>Mark</value>
</property>
<type-tref>/types/type1</type-tref>
</object>
<object id="objectF">
<property>
<value>David</value>
</property>
<type-tref>/types/type3</type-tref>
</object>
</specialObjects>
<types>
<type id="type1">
<name>myFirst</name>
<color>blue</color>
<format>circle</format>
</type>
<type id="type2">
<name>mySecond</name>
<color>red</color>
<format>rectangle</format>
</type>
<type id="type3">
<name>myThird</name>
<color>black</color>
<format>empty</format>
</type>
</types>
</top-level>
现在我的XSLT脚本应该处理输入XML文件,以便在输出中只引用一次引用类型('type1','type2'和'type3')(包含完整的详细信息)(仅在示例中为'objectA','objectB'和'objectF'(虽然它们有第一个引用),而对于其他情况只使用href。
<file>
<person id="objectA">
<name>Jack</name>
<myType id="1">
<myName>myFirst</myName>
<myColor>blue</myColor>
<myFormat>circle</myFormat>
</myType>
</person>
<person id="objectB">
<name>Jim</name>
<myType id="2">
<myName>mySecond</myName>
<myColor>red</myColor>
<myFormat>rectangle</myFormat>
</myType>
</person>
<person id="objectC">
<name>John</name>
<myType href="#1"></myType>
</person>
<person id="objectD">
<name>Jill</name>
<myType href="#1"></myType>
</person>
<person id="objectE">
<name>Mark</name>
<myType href="#1"></myType>
</person>
<person id="objectF">
<name>David</name>
<myType id="2">
<myName>myThird</myName>
<myColor>black</myColor>
<myFormat>empty</myFormat>
</myType>
</person>
</file>
如何知道元素是否已被处理(现在只需要引用)?
或者我应该以某种方式收集所有对象(来自“group(s)”以及“specialObjects”元素)? (只是为了避免在浏览源中的对象时为每个person-element生成'myType'信息n次的情况)
任何帮助/提示真的很感激!
答案 0 :(得分:2)
这是在type-tref
元素上定义键的好例子,然后当你点击它时,你可以检查它是否是第一次提到该键值并采取适当的行动。您可以使用其他密钥为每个type
type-tref
<xsl:key name="trefKey" match="type-tref" use="." />
<xsl:key name="typeById" match="type" use="@id" />
<xsl:template match="type-tref[. is key('trefKey', .)[1]]">
<xsl:apply-templates select="key('typeById', tokenize(., '/')[last()])" />
</xsl:template>
<xsl:template match="type-tref">
<myType href="#{generate-id(key('typeById', tokenize(., '/')[last()]))}" />
</xsl:template>
<xsl:template match="type">
<myType id="{generate-id()}">
<!-- insert child elements here -->
</myType>
</xsl:template>
这里我只是使用generate-id
的结果作为ID属性,所以它们不一定是序列号,但它们在内部是一致的(这不一定是坏事 - “ID “在XML意义上应该是有效的名称,这尤其意味着它们不应该以数字开头。”
答案 1 :(得分:1)
您可以使用密钥并识别第一个object
:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="by-type" match="object" use="type-tref"/>
<xsl:key name="ref-type" match="type" use="@id"/>
<xsl:output indent="yes"/>
<xsl:variable name="types" select="//types/type"/>
<xsl:template match="top-level">
<file>
<xsl:apply-templates select="//object"/>
</file>
</xsl:template>
<xsl:template match="object[generate-id() = generate-id(key('by-type', type-tref)[1])]">
<person id="{@id}">
<name><xsl:value-of select="property/value"/></name>
<xsl:apply-templates select="key('ref-type', tokenize(type-tref, '/')[last()])"/>
</person>
</xsl:template>
<xsl:template match="object[not(generate-id() = generate-id(key('by-type', type-tref)[1]))]">
<person id="{@id}">
<name><xsl:value-of select="property/value"/></name>
<myType href="#{index-of($types, key('ref-type', tokenize(type-tref, '/')[last()]))}"/>
</person>
</xsl:template>
<xsl:template match="type">
<myType>
<xsl:attribute name="id"><xsl:number/></xsl:attribute>
<xsl:apply-templates/>
</myType>
</xsl:template>
<xsl:template match="type//*">
<xsl:element name="my{upper-case(substring(local-name(), 1, 1))}{substring(local-name(), 2)}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
输出
<file>
<person id="objectA">
<name>Jack</name>
<myType id="1">
<myName>myFirst</myName>
<myColor>blue</myColor>
<myFormat>circle</myFormat>
</myType>
</person>
<person id="objectB">
<name>Jim</name>
<myType id="2">
<myName>mySecond</myName>
<myColor>red</myColor>
<myFormat>rectangle</myFormat>
</myType>
</person>
<person id="objectC">
<name>John</name>
<myType href="#1"/>
</person>
<person id="objectD">
<name>Jill</name>
<myType href="#1"/>
</person>
<person id="objectE">
<name>Mark</name>
<myType href="#1"/>
</person>
<person id="objectF">
<name>David</name>
<myType id="3">
<myName>myThird</myName>
<myColor>black</myColor>
<myFormat>empty</myFormat>
</myType>
</person>
</file>
[编辑] Ian有同样的想法,但使用了更优雅的XSLT 2.0表达方式,因此我将使用is
运算符来检查身份,发布我的样式表的编辑:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="by-type" match="object" use="type-tref"/>
<xsl:key name="ref-type" match="type" use="@id"/>
<xsl:output indent="yes"/>
<xsl:variable name="types" select="//types/type"/>
<xsl:template match="top-level">
<file>
<xsl:apply-templates select="//object"/>
</file>
</xsl:template>
<xsl:template match="object[. is key('by-type', type-tref)[1]]">
<person id="{@id}">
<name><xsl:value-of select="property/value"/></name>
<xsl:apply-templates select="key('ref-type', tokenize(type-tref, '/')[last()])"/>
</person>
</xsl:template>
<xsl:template match="object[not(. is key('by-type', type-tref)[1])]">
<person id="{@id}">
<name><xsl:value-of select="property/value"/></name>
<myType href="#{index-of($types, key('ref-type', tokenize(type-tref, '/')[last()]))}"/>
</person>
</xsl:template>
<xsl:template match="type">
<myType>
<xsl:attribute name="id"><xsl:number/></xsl:attribute>
<xsl:apply-templates/>
</myType>
</xsl:template>
<xsl:template match="type//*">
<xsl:element name="my{upper-case(substring(local-name(), 1, 1))}{substring(local-name(), 2)}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>