2013-07-28 3 views
1

Прежде всего, мои извинения за заголовок, но я не знаю, как хорошо описать мою проблему. Поэтому образец кода сделает вещи более ясными.многозначная группировка с xslt 2

Предположим, у меня есть следующий XML-дерево:

<root> 
<node> 
<value xml:lang="en">Some English Content</value> 
<value xml:lang="fr">Some French Content</value> 
<value xml:lang="de">Some German Content</value> 
</node> 
<node> 
<value xml:lang="en">Some English Content</value> 
<value xml:lang="de">Some German Content</value> 
</node> 
<node> 
<value xml:lang="en">Some Other English Content</value> 
<value xml:lang="fr">Some Other French Content</value> 
<value xml:lang="de">Some Other German Content</value> 
</node> 
<node> 
<value xml:lang="en">Some English Content</value> 
<value xml:lang="fr">Some French Content</value> 
<value xml:lang="de">Some German Content</value> 
</node> 
<node> 
<value xml:lang="fr">Some French Content</value> 
<value xml:lang="de">Some German Content</value> 
</node> 
</root> 

Так в основном существуют различные nodesets, с числом локализованных строк, и я хочу, чтобы сгруппировать эти наборы на основе содержания. Узел 1,2,4 и 5 относится к одной теме, но не все строки могут быть доступны во всех локалях, поэтому я не могу использовать ссылочную строку (скажем, английский, поскольку она недоступна в узле 5). Узел 3 содержит различный контент, поэтому он должен быть частью другой группы.

Звуки, вероятно, довольно сложно, но это результат я хотел бы получить (с помощью XSLT 2):

<values> 
<group> 
    <value xml:lang="en">Some English Content</value> 
    <value xml:lang="fr">Some French Content</value> 
    <value xml:lang="de">Some German Content</value> 
</group> 
<group> 
    <value xml:lang="en">Some Other English Content</value> 
    <value xml:lang="fr">Some Other French Content</value> 
    <value xml:lang="de">Some Other German Content</value> 
</group> 
</values> 

Любая идея о том, как лучше справиться с этим? Обратите внимание, что я могу иметь до 40 различных языков в узле и может иметь сотни узлов в файле, поэтому ресурсы также могут стать проблемой.

+0

Что происходит, если существует такой элемент, как '<значение XML: LANG = "FR"> Некоторые другие французские содержание<значение XML: LANG = "де"> Некоторые немецкий содержание' ? –

ответ

0

Я не уверен, я полностью понимаю ваше требование, но я написал код, который первым пытается заполнить node элементов с value с пропавшими языками, а затем в группу на заполненных элементах:

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:mf="http://example.com/mf" 
    exclude-result-prefixes="xs mf"> 

<xsl:param name="sep" as="xs:string" select="'|'"/> 

<xsl:output indent="yes"/> 

<xsl:variable name="main-doc" select="/"/> 

<xsl:variable name="languages" as="xs:string*"> 
    <xsl:perform-sort select="distinct-values(root/node/value/@xml:lang)"> 
    <xsl:sort select="."/> 
    </xsl:perform-sort> 
</xsl:variable> 

<xsl:key name="k1" match="node/value" use="concat(@xml:lang, $sep, .)"/> 

<xsl:template match="root"> 
    <values> 
    <xsl:variable name="filled" as="element(node)*"> 
     <xsl:apply-templates select="node" mode="fill"/> 
    </xsl:variable> 
    <xsl:for-each-group select="$filled" group-by="string-join(value, $sep)"> 
     <group> 
     <xsl:copy-of select="value"/> 
     </group> 
    </xsl:for-each-group> 
    </values> 
</xsl:template> 

<xsl:template match="node" mode="fill"> 
    <xsl:copy> 
    <xsl:variable name="this" as="element(node)" select="."/> 
    <xsl:for-each select="$languages"> 

     <value xml:lang="{.}"> 
     <xsl:value-of 
      select="if ($this/value[lang(current())]) 
        then $this/value[lang(current())] 
        else (key('k1', 
          concat($this/value[1]/@xml:lang, $sep, $this/value[1]), 
          $main-doc)/../value[lang(current())])[1]"/> 
     </value> 
    </xsl:for-each> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

Как вы можете видеть, мне нужен был какой-то заказ, чтобы заполнить элементы, поэтому я отсортировал их по отдельному @xml:lang, я не уверен, что вы этого хотите. При таком подходе выход для входа вы публикуемый с Saxon 9.5

<values> 
    <group> 
     <value xml:lang="de">Some German Content</value> 
     <value xml:lang="en">Some English Content</value> 
     <value xml:lang="fr">Some French Content</value> 
    </group> 
    <group> 
     <value xml:lang="de">Some Other German Content</value> 
     <value xml:lang="en">Some Other English Content</value> 
     <value xml:lang="fr">Some Other French Content</value> 
    </group> 
</values> 

Я также не уверен, какая стратегия является намеченной один для заполнения элементов (см также комментарий я отправил). В конце концов я решил указать элементы node/value на конкатенацию @xml:lang и их содержимое, а затем при заполнении элементов, где отсутствует язык, я просто сопоставляю первый value ребенок node. Так что в основном это означает, что если у какого-то node есть первый value xml:lang="foo" с содержанием bar, то матч просто на этом языке foo и содержание bar, и мы скопируем содержимое value на недостающий язык.

Если вы не хотите, сортировкой и вы можете жить с порядком исходной последовательности атрибутов, то вы можете опустить сортировку, то таблица стилей, то есть

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:mf="http://example.com/mf" 
    exclude-result-prefixes="xs mf"> 

<xsl:param name="sep" as="xs:string" select="'|'"/> 

<xsl:output indent="yes"/> 

<xsl:variable name="main-doc" select="/"/> 

<xsl:variable name="languages" as="xs:string*" select="distinct-values(root/node/value/@xml:lang)"/> 

<xsl:key name="k1" match="node/value" use="concat(@xml:lang, $sep, .)"/> 

<xsl:template match="root"> 
    <values> 
    <xsl:variable name="filled" as="element(node)*"> 
     <xsl:apply-templates select="node" mode="fill"/> 
    </xsl:variable> 
    <xsl:for-each-group select="$filled" group-by="string-join(value, $sep)"> 
     <group> 
     <xsl:copy-of select="value"/> 
     </group> 
    </xsl:for-each-group> 
    </values> 
</xsl:template> 

<xsl:template match="node" mode="fill"> 
    <xsl:copy> 
    <xsl:variable name="this" as="element(node)" select="."/> 
    <xsl:for-each select="$languages"> 

     <value xml:lang="{.}"> 
     <xsl:value-of 
      select="if ($this/value[lang(current())]) 
        then $this/value[lang(current())] 
        else (key('k1', 
          concat($this/value[1]/@xml:lang, $sep, $this/value[1]), 
          $main-doc)/../value[lang(current())])[1]"/> 
     </value> 
    </xsl:for-each> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

Таким образом, выход, как вы спросил:

<values> 
    <group> 
     <value xml:lang="en">Some English Content</value> 
     <value xml:lang="fr">Some French Content</value> 
     <value xml:lang="de">Some German Content</value> 
    </group> 
    <group> 
     <value xml:lang="en">Some Other English Content</value> 
     <value xml:lang="fr">Some Other French Content</value> 
     <value xml:lang="de">Some Other German Content</value> 
    </group> 
</values> 
+0

Спасибо Мартин. В конце концов, это не очень хорошо отразилось на моем реальном живом контенте, но вы наверняка дали мне правильный путь. – Wokoman

Смежные вопросы