2016-12-04 4 views
1

У меня есть этот MODSXSLT выбрать подэлемент принять

<modsCollection> 
    <mods ID="master"> 
    <originInfo> 
     <dateOther encoding="8601" type="publication">2016</dateOther> 
    </originInfo> 
    <originInfo> 
     <dateOther encoding="8601" type="onlineFirst">2015</dateOther> 
    </originInfo> 
    <originInfo> 
     <dateOther encoding="8601" type="accepted">2014</dateOther> 
    </originInfo> 
    <originInfo> 
     <dateOther encoding="8601" type="submitted">2013</dateOther> 
    </originInfo> 
    </mods> 
</modsCollection> 

Мне нужно преобразовать этот элемент dateOther и эквивалентный постоянный ток: дата, но я должен взять только один dateOther в зависимости от атрибута типа (если существует публикация, в которой мы принимаем эту дату. Другое, если это не так, как мы делаем onFirstFirst, если она не существует, что принято и так далее).

Таким образом, в случае верхних MODS мы создали бы

<dc:date>2016</dc:date> 

потому dateOther с атрибутом типа = публикации существовало, если бы не мы бы тип = onlineFirst и т.д.

Порядок датыДругая внешность может быть разной, и может быть только один или несколько из них.

Проблема начинается с трансформации, я не могу выбрать все элементы originInfo/dateOther, так что я могу сравнить их выбрать

<xsl:template match="//mods:mods[@ID = 'master']/mods:originInfo/mods:dateOther"> 
    <xsl:choose> 
    <xsl:when test="..."> 

     <dc:date>...</dc:date> 

    </xsl:when> 
    ... 
    </xsl:choose> 
</xsl:template> 

Любая помощь, чтобы решить это было бы полезно.

+0

Укажите, используете ли вы XSLT 1.0 или 2.0. –

ответ

0

Вы можете использовать либо xsl:apply-templates, либо xsl:for-each, чтобы отсортировать их по правильному приоритету, а затем выбрать первый (ПРИМЕЧАНИЕ: вы не сказали нам, какие пространства имен вы используете, поэтому я не использую пространства имен в моем примере):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 

    <xsl:template match="mods[@ID = 'master']"> 
    <xsl:apply-templates select="originInfo/dateOther"> 
     <xsl:sort select="substring-before(
          '|publication|onlineFirst|accepted|submitted|', 
          concat('|', @type, '|') 
         )" /> 
    </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="dateOther"> 
     <xsl:if test="position() = 1"> 
     <date><xsl:value-of select="." /></date> 
     </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Хорошая попытка, но есть проблема. Это приведет к неправильному результату, если значение для атрибута «type» может быть подстрокой другого значения. Подсказка: разграничение должно быть * с обеих сторон * и по обоим аргументам. Кроме того, нет необходимости использовать 'string-length()' –

+1

@DimitreNovatchev. Я знал об ошибке подстроки, но предположил, что OP имеет дискретное число неперекрывающихся значений. Я исправил это сейчас, и теперь я вижу, почему длина строки не нужна. Спасибо, что поставил меня на ноги. – JLRishe

+0

JLRishe, добро пожаловать! –

-1

Если вы хотите выбрать один из разных дат, вы должны сделать это из контекста своего родителя. В данном примере это может выглядеть примерно так:

<xsl:template match="mods[@ID = 'master']"> 
    <!-- other stuff? --> 
    <date> 
     <xsl:choose> 
      <xsl:when test="originInfo/dateOther[@type='publication']"> 
       <xsl:value-of select="originInfo/dateOther[@type='publication']"/> 
      </xsl:when> 
      <xsl:when test="originInfo/dateOther[@type='onlineFirst']"> 
       <xsl:value-of select="originInfo/dateOther[@type='onlineFirst']"/> 
      </xsl:when> 
      <xsl:when test="originInfo/dateOther[@type='accepted']"> 
       <xsl:value-of select="originInfo/dateOther[@type='accepted']"/> 
      </xsl:when> 
      <!-- and so on --> 
     </xsl:choose> 
    </date> 
    <!-- more stuff? --> 
</xsl:template> 

Предполагается, что XSLT 1.0. Есть дополнительные опции в XSLT 2.0 - например:

<xsl:template match="mods[@ID = 'master']"> 
    <!-- other stuff? --> 
    <date> 
     <xsl:value-of select="(originInfo/dateOther[@type='publication'], originInfo/dateOther[@type='onlineFirst'], originInfo/dateOther[@type='accepted'], originInfo/dateOther[@type='submitted'])[1]"/> 
    </date> 
    <!-- more stuff? --> 
</xsl:template> 
0

Как просто, как это - обратите внимание, что вам не нужно использовать string-length() функцию:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:dc="my:dc"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:variable name="vTypes" select="'|publication|onlineFirst|accepted|submitted|'"/> 

    <xsl:template match="mods"> 
    <dc:date> 
     <xsl:for-each select="originInfo/dateOther"> 
     <xsl:sort select="substring-before($vTypes, concat('|', @type, '|'))"/> 
     <xsl:variable name="vPos" select="position()"/> 
     <xsl:value-of select="self::node()[$vPos=1]"/> 
     </xsl:for-each> 
    </dc:date> 
    </xsl:template> 
</xsl:stylesheet> 

Когда это преобразование применяется к следующему XML-документу (предоставленному, но элементы перетасовываются таким образом, чтобы результат не был сверху):

<modsCollection> 
    <mods ID="master"> 
    <originInfo> 
     <dateOther encoding="8601" type="onlineFirst">2015</dateOther> 
    </originInfo> 
    <originInfo> 
     <dateOther encoding="8601" type="accepted">2014</dateOther> 
    </originInfo> 
    <originInfo> 
     <dateOther encoding="8601" type="publication">2016</dateOther> 
    </originInfo> 
    <originInfo> 
     <dateOther encoding="8601" type="submitted">2013</dateOther> 
    </originInfo> 
    </mods> 
</modsCollection> 

разыскиваемый, правильный результат получается:

<dc:date xmlns:dc="my:dc">2016</dc:date> 

XSLT 2.0:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:dc="my:dc"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

    <xsl:variable name="vTypes" select="'publication','onlineFirst','accepted','submitted'"/> 


    <xsl:template match="mods"> 
    <dc:date> 
     <xsl:sequence select= 
     "*/*[index-of($vTypes,@type)[1] 
      eq min(current()/*/*/index-of($vTypes,@type)[1])]/text()"/> 
    </dc:date> 
    </xsl:template> 
</xsl:stylesheet> 

Примечание:

Я настоятельно рекомендую, чтобы попытаться избежать использования XSLT условные инструкции таких как <xsl:if>, <xsl:choose>, <xsl:when> ... и т. д.Использование их часто приводит к запутанному, не легко понятному, спагетти-подобному коду и является анти-шаблоном в обоих процедурных/императивных и декларативных/функциональных стилях программирования.