2017-02-14 15 views
1

Я хочу скопировать элемент, но добавить к нему новый атрибут (@inTheList), значение которого определяет атрибуты, имя которых указано в данном списке.Определите, какие имена атрибутов элемента соответствуют списку. Поместите результат в новый атрибут

Вход:

<element head="this" body="that" foot="the other"> 

и список [ "рука", "нос", "тело", "голова"] как-то представлены.

Выход:

<element head="this" body="that" foot="the other" inTheList="head body"> 

Там, наверное, умный XSLT-иш способ сделать это, но сейчас я не могу даже думать о грубой силы пути.

ответ

1

Предполагая XSLT 2.0 вы можете использовать

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"> 

    <xsl:param name="names" as="xs:string*" select='"arm", "nose", "body", "head"'/> 

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[@*/name() = $names]"> 
     <xsl:copy> 
      <xsl:copy-of select="@*"/> 
      <xsl:attribute name="inTheList" select="$names[. = current()/@*/name()]"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:transform> 

Online на http://xsltransform.net/bFWR5EN.

+0

Это хорошо, но мне интересно, будет ли оно медленным, если $ names имеет десятки или сотни предметов. $ названия [. = current()/@ */name()] сканирует весь список, даже если нет необходимости проверять, правильно? – JPM

+0

Он отлично работал. Нет проблем с производительностью. Спасибо, сэр! – JPM

+0

Ну, это действительно создает проблему с производительностью, увеличивая время обработки до 2,5x. – JPM

1

Предполагая, что вход так:

<root> 
    <element head="this" nose="whatever" body="that" foot="the other"/> 
    <element arm="this" body="that" foot="the other"/> 
    <element foot="that"/> 
</root> 

Затем этот шаблон будет делать то, что вы хотите (объяснение в комментариях):

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

    <xsl:output indent="yes"/> 

    <!-- Change this to match the elements you need to update --> 
    <xsl:template match="/"> 
     <out> 
      <xsl:apply-templates select="root/element"/> 
     </out> 
    </xsl:template> 

    <!-- copy an "element" and add a "theList" attribute listing selected attributes that are present --> 
    <xsl:template match="element"> 
     <!-- This variable selects the target attributes --> 
     <xsl:variable name="names"> 
      <xsl:apply-templates select="@arm|@nose|@body|@head"/> 
     </xsl:variable> 
     <xsl:copy> 
      <!-- copy all attributes --> 
      <xsl:copy-of select="@*"/> 
      <!-- add the new attribute if any of the target names are present --> 
      <xsl:if test="$names != ''"> 
       <xsl:attribute name="theList"> 
        <xsl:value-of select="normalize-space($names)"/> 
       </xsl:attribute> 
      </xsl:if> 
     </xsl:copy> 
    </xsl:template> 

    <!-- attribute name + a space separator --> 
    <xsl:template match="element/@*"> 
     <xsl:value-of select="concat(name(), ' ')"/> 
    </xsl:template> 

</xsl:stylesheet> 

Выход:

<out> 
    <element head="this" 
      nose="whatever" 
      body="that" 
      foot="the other" 
      theList="head nose body"/> 
    <element arm="this" body="that" foot="the other" theList="arm body"/> 
    <element foot="that"/> 
</out> 
1

Похоже, вы могли бы (при условии XSLT 1.0):

<xsl:template match="element"> 
    <xsl:copy> 
     <xsl:copy-of select="@*"/> 
     <xsl:attribute name="theList"> 
      <xsl:for-each select="@head | @body | @arm | @nose"> 
       <xsl:value-of select="name()"/> 
       <xsl:if test="position() != last()"> 
        <xsl:text> </xsl:text> 
       </xsl:if> 
      </xsl:for-each> 
     </xsl:attribute> 
     <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:template> 

Возможно, вы захотите использовать это вместе с шаблоном преобразования .