2009-08-04 3 views
3

У меня есть исходный документ:XSLT Вставить элемент, если он не существует

<?xml version="1.0"?> 
<source> 
    <ItemNotSubstituted/> 
    <ItemToBeSubstituted Id='MatchId' /> 
</source> 

И таблицы стилей, содержащий текст, который я хочу, чтобы заменить в источник:

<?xml version="1.0"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" method="xml" omit-xml-declaration="no" version="1.0"/> 
    <xsl:preserve-space elements="//*"/> 

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

    <xsl:template match="ItemToBeSubstituted[@Id = 'MatchId']"> 
    <xsl:copy> 
     <xsl:copy-of select="@*|*"/> 
     <Element1/> 
     <Element2 Value="foo"/> 
     <Element3 Value="bar"/> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Эта таблица стилей succesfuly копий <Element1/><Element2 Value="foo"/><Element3 Value="bar"/> - ItemToBeSubstituted. Но когда я использую другой исходный документ, в котором ItemToBeSubstituted уже имеет содержание:

<?xml version="1.0"?> 
<source> 
    <ItemNotSubstituted/> 
    <ItemToBeSubstituted Id='MatchId'> 
    <Element3 Value="baz"/> 
    </ItemToBeSubstituted> 
</source> 

я получаю этот выход:

<?xml version="1.0"?> 
<source> 
    <ItemNotSubstituted/> 
    <ItemToBeSubstituted Id="MatchId"> 
    <Element3 Value="baz"/> 
    <Element1/> 
    <Element2 Value="foo"/> 
    <Element3 Value="bar"/> 
    </ItemToBeSubstituted> 
</source> 

Я хотел бы только заменить элементы из таблицы стилей, которые уже не существуют в исходном документе. Это выход я искал после применения стилей ко второму документу, с только <Element3> элемента из исходного документа:

<?xml version="1.0"?> 
<source> 
    <ItemNotSubstituted/> 
    <ItemToBeSubstituted Id="MatchId"> 
    <Element3 Value="baz"/> 
    <Element1/> 
    <Element2 Value="foo"/> 
    </ItemToBeSubstituted> 
</source> 

Каков наилучший подход для этого с помощью XSL? Таблица стилей может содержать множество элементов, подлежащих замене. Поэтому я не хочу использовать подход, который требует <xsl:if> вокруг каждого элемента. Есть ли лучший способ, чем использовать одну таблицу стилей для вставки содержимого, а затем вторую таблицу стилей, которая удаляет повторяющиеся элементы?

ответ

2

Это XSLT 1.0 решение делает то, что вы собираетесь:

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

    <!-- expand this section to contain all your default elements/values --> 
    <subst:defaults> 
    <subst:element name="ItemToBeSubstituted" id="MatchId"> 
     <subst:Element1/> 
     <subst:Element2 Value="foo"/> 
     <subst:Element3 Value="bar"/> 
    </subst:element> 
    </subst:defaults> 

    <!-- this makes the above available as a variable --> 
    <xsl:variable name="defaults" select="document('')/*/subst:defaults" /> 

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

    <!-- expand the match expression to contain all elements 
     names that need default values --> 
    <xsl:template match="ItemToBeSubstituted"> 
    <xsl:copy> 
     <xsl:copy-of select="@*|*"/> 
     <xsl:call-template name="create-defaults" /> 
    </xsl:copy> 
    </xsl:template> 

    <!-- this does all the heavy lifting --> 
    <xsl:template name="create-defaults"> 
    <xsl:variable name="this" select="." /> 

    <xsl:for-each select=" 
     $defaults/subst:element[@name = name($this) and @id = $this/@Id]/* 
    "> 
     <xsl:if test="not($this/*[name() = local-name(current())])"> 
     <xsl:apply-templates select="." /> 
     </xsl:if> 
    </xsl:for-each> 
    </xsl:template> 

    <!-- create the default nodes without namespaces --> 
    <xsl:template match="subst:*"> 
    <xsl:element name="{local-name()}"> 
     <xsl:apply-templates select="subst:*|@*" /> 
    </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

Использование отдельного пространства имен («SUBST») позволяет сохранить настройки по умолчанию в таблице стилей. Является ли это хорошей вещью или нет, зависит, по крайней мере, вам не нужно иметь два файла.

Если вы предпочитаете, чтобы таблица стилей была отделена от значений по умолчанию, поместите их в дополнительный файл и используйте эту строку.

<xsl:variable name="defaults" select="document('defaults.xml')/subst:defaults" /> 

Вы могли бы бросить все обработки дополнительное пространство имен, как только вы сделаете это, и будет в конечном итоге с решением Джош Дэвис предложенного, более или менее.

2

Я хотел бы использовать что-то вроде этого:

<?xml version="1.0"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes" method="xml" omit-xml-declaration="no" version="1.0"/> 
    <xsl:preserve-space elements="//*"/> 

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

    <xsl:template match="ItemToBeSubstituted[@Id = 'MatchId']"> 
    <xsl:variable name="node" select="." /> 
    <xsl:copy> 
     <xsl:copy-of select="@*|*"/> 

     <xsl:for-each select="document('elements.xml')/elements/*"> 
     <xsl:if test="not($node/*[name() = name(current())])"> 
      <xsl:copy-of select="." /> 
     </xsl:if> 
     </xsl:for-each> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Где Elements.xml это файл, в котором вы храните какие элементы для добавления по умолчанию

<?xml version="1.0" encoding="utf-8" ?> 
<elements> 
    <Element1/> 
    <Element2 Value="foo"/> 
    <Element3 Value="bar"/> 
</elements> 

Использование <for-each> мы перебирать элементы по умолчанию, проверьте, есть ли элемент с этим именем в качестве дочернего элемента текущего узла, а затем добавьте его, если его нет.

+0

+1 Это хорошее начало в этой проблеме. Он мог бы использовать немного больше обобщения вокруг того факта, что, очевидно, может быть больше элементов, которые нужно заменить, чем 'ItemToBeSubstected [@Id = 'MatchId']', но это не так сложно сделать. – Tomalak

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