XSLT 2.0报告元素只有一次

时间:2014-01-11 13:02:43

标签: xslt-2.0

我遇到了以下XSLT 2.0处理问题: 如何获取元素(在下一个示例输入中命名为'type')仅报告完整信息一次,然后仅报告参考(href)?

示例输入XML

<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次的情况)

任何帮助/提示真的很感激!

2 个答案:

答案 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>