我在people.xml
中有一个人员列表,其中包含对属性@trait
和@rel
中的家庭关系的引用。因此,条目递归到列表,其中@rel
包含@xml:id
。
<person xml:id="person_a">
<firstname>John</firstname>
<lastname>Foo</lastname>
<trait type="spouse_of" rel="#person_b">
<trait type="parent_of" rel="#person_c #person_d">
<person>
<person xml:id="person_b">
<firstname>Sarah</firstname>
<lastname>Foo</lastname>
<trait type="spouse_of" rel="#person_a">
<trait type="parent_of" rel="#person_c #person_d">
<person>
<person xml:id="person_c">
<firstname>Henry</firstname>
<lastname>Foo</lastname>
<trait type="child_of" rel="#person_a #person_b">
<trait type="sibling_of" rel="#person_d">
<person>
<person xml:id="person_d">
<firstname>Tom</firstname>
<lastname>Foo</lastname>
<trait type="child_of" rel="#person_a #person_b">
<trait type=sibling_of" rel="#person_c">
<person>
....
使用XSL 3.0 / Saxon,我试图将家庭关系输出为以下格式:
<perslist>
<person>
<name>John Foo</name>
<relation>spouse of Sarah Foo</relation>
<relation>parent of Henry Foo, Tom Foo</relation>
</person>
<person>
<name>Sarah Foo</name>
<relation>spouse of John Foo</relation>
<relation>parent of Henry Foo, Tom Foo</relation>
</person>
<person>
<name>Henry Foo</name>
<relation>child of John Foo, Sarah Foo</relation>
<relation>sibling of Tom Foo</relation>
</person>
<person>
<name>Tom Foo</name>
<relation>child of John Foo, Sarah Foo</relation>
<relation>sibling of Henry Foo</relation>
</person>
...
</perslist>
大部分内容已完成并正常运行,但我无法使用@rel
,因为它可能包含多个值。
我正在使用密钥来查找xml:id
。我正在尝试使用tokenize()
拆分@rel
中包含的ID,但我没有取得任何成功。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform version="3.0">
<xsl:key name="ids" match="person" use="@xml:id"/>
....
<xsl:template match="trait">
<xsl:variable name="trt" select="."/>
<xsl:choose>
<xsl:when test=".[@type='spouse_of']">
<relation>spouse of
<xsl:for-each select="tokenize($trt/@rel, ' ')">
<xsl:value-of select="key('ids',substring-after(.,'#'))/firstname, key('ids',substring-after(.,'#'))/lastname" separator=", "/>
</xsl:for-each>
</relation>
</xsl:when>
<xsl:when test=".[@type='parent_of']">
<relation>parent of
<xsl:for-each select="tokenize($trt/@rel, ' ')">
<xsl:value-of select="key('ids',substring-after(.,'#'))/firstname, key('ids',substring-after(.,'#'))/lastname" separator=", ">
</xsl:for-each>
</relation>
</xsl:when>
<xsl:when test=".[@type='child_of']">
<relation>child of
<xsl:for-each select="tokenize($trt/@rel, ' ')">
<xsl:value-of select="key('ids',substring-after(.,'#'))/firstname, key('ids',substring-after(.,'#'))/lastname" separator=", ">
</xsl:for-each>
</relation>
</xsl:when>
</xsl:template>
</xsl:stylesheet>
特别是Saxon告诉我&#34;当上下文项不是节点时,不能调用key()函数&#34;
感谢您的任何建议。
的Nb。更正了xml和xsl错误
答案 0 :(得分:2)
在<xsl:for-each>
中更改了上下文项。
当您迭代由tokenize()
生成的标记列表时,每次迭代期间的上下文项将不是节点,而是xs:string
。
key()
期望上下文项是一个节点。这是因为<xsl:key>
始终适用于所有打开的文档,并且上下文项决定从哪个文档中选择匹配节点。如果您未在key()
的第三个参数中提供显式上下文项,则假定上下文项的文档元素。在这种特殊情况下,.
不是一个节点,它不属于任何文档,因此key()
会混淆。
这可以通过显式传递有效的上下文项来解决。将文档元素(右侧文档!)存储在顶级变量中,让我们说$doc
,并在调用key()
时使用它。 包含所需匹配项的任何节点都可以使用。
话虽如此,你做了太多的复制粘贴编程。怎么样:
<xsl:key name="person" match="person" use="@xml:id"/>
<xsl:variable name="doc" select="/*" />
<xsl:template match="trait">
<xsl:variable name="self" select="." />
<xsl:for-each select="tokenize(normalize-space(@rel), ' ')">
<relation>
<xsl:choose>
<xsl:when test="$self/@type='spouse_of'">spouse of </xsl:when>
<xsl:when test="$self/@type='parent_of'">parent of </xsl:when>
<xsl:when test="$self/@type='child_of'">child of </xsl:when>
<!-- there probably should be an <xsl:otherwise> here -->
</xsl:choose>
<xsl:variable name="p" select="key('person', substring-after(., '#'), $doc)" />
<xsl:value-of select="$p/lastname, $p/firstname" separator=", " />
</relation>
</xsl:for-each>
</xsl:template>
您可以通过更广泛地使用模板来保存行,临时变量并使方法更加模块化(想想国际化)。
<xsl:key name="personByRef" match="person" use="concat('#', @xml:id)" />
<xsl:variable name="doc" select="/*" />
<xsl:template match="trait">
<xsl:variable name="self" select="." />
<xsl:for-each select="tokenize(normalize-space(@rel), ' ')">
<relation>
<xsl:apply-templates select="$self/@type" mode="label" />
<xsl:apply-templates select="key('personByRef', ., $doc)" mode="fullname" />
</relation>
</xsl:for-each>
</xsl:template>
<xsl:template match="trait/@type[.='spouse_of']" mode="label">spouse of </xsl:template>
<xsl:template match="trait/@type[.='parent_of']" mode="label">parent of </xsl:template>
<xsl:template match="trait/@type[.='child_of']" mode="label">child of </xsl:template>
<xsl:template match="person" mode="fullname">
<xsl:value-of select="lastname,firstname" separator=", " />
</xsl:template>
此处可以从特定语言的文件导入整个"label"
模板块,而无需触及您的逻辑。
也许您想要在其他地方输出全名 - 也可以使用一个专用模板。