токенизировать список идентификаторов и искать их с помощью ключа - PullRequest
0 голосов
/ 05 июня 2018

У меня есть список людей в 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 s.Я пытаюсь использовать tokenize(), чтобы разделить идентификаторы, содержащиеся в @rel, но у меня ничего не получается.

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

В частности, Саксон говорит мне: «Невозможно вызвать функцию key (), когдаэлемент контекста не является узлом "

Спасибо за любые предложения.

Nb.исправлены ошибки xml и xsl

1 Ответ

0 голосов
/ 05 июня 2018

Элемент контекста изменен на <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" шаблонов может быть импортирован из файла для конкретного языка, не затрагивая вашу логику.

Может быть, вы хотите вывести полные имена и в другом месте - наличие одного выделенного шаблона для этого полезно, так какхорошо.

...