2014-10-28 2 views
0

Мне нужно обновить множество xml-файлов с простой конфигурацией. Проблема, которая у меня есть - элемент config является необязательной в моих xml-файлах, и они могут уже иметь некоторую конфигурацию.XSLT: вставлять элементы, если их нет

Так что я хочу сделать:

  1. Вставка <config/> со всеми предопределенными element «с, если он отсутствует
  2. Вставка отсутствует element в config тег. Если бы они уже были там, то оставайся как есть.

Перед:

<root> 
    <config> <!-- this is optional. can be not defined at all --> 
    <element2 attr="c"/> 
    </config> 
</root> 

То, что я хочу, чтобы получить:

<root> 
    <config> 
    <element1 attr="a"/> 
    <element2 attr="b"/> <!-- not override this one, but insert if missing --> 
    <element3 attr="c"/> 
    </config> 
</root> 

Так что мои мысли были иметь несколько шаблонов и применять 1-ый шаг, если это не существует, и делаем второй уровень в пределах отдельный mode. Но это не сработало.

UPD. Я использую xslt 1.0, но я предполагаю, что переход на 2.0 не будет проблемой.

Предопределенные элементы:

<element1 attr="a"/> 
<element2 attr="b"/> 
<element3 attr="c"/> 
+0

Какую версию XSLT вы используете? Какие именно предопределенные элементы вам нужно добавить? –

ответ

2

ИМХО, вы должны смотреть на это с противоположного конца; который подавляет любые существующие config и устанавливает их самостоятельно, используя либо существующие значения из оригинала config, либо - если их не найдено - ваши значения по умолчанию.

Вот XSLT 1.0 реализация:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:exsl="http://exslt.org/common" 
extension-element-prefixes="exsl"> 
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:key name="cfg" match="config/*" use="local-name()" /> 

<xsl:variable name="default-cfg"> 
    <element1 attr="a"/> 
    <element2 attr="b"/> 
    <element3 attr="c"/> 
</xsl:variable> 

<xsl:variable name="root" select="/"/> 

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

<xsl:template match="root"> 
    <xsl:copy> 
     <config> 
      <xsl:for-each select="exsl:node-set($default-cfg)/*"> 
       <xsl:call-template name="cfg-element"/> 
      </xsl:for-each> 
     </config> 
     <xsl:apply-templates select="node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="config"/> 

<xsl:template name="cfg-element"> 
    <xsl:variable name="name" select="name()"/> 
    <xsl:variable name="default-value" select="@attr"/> 
    <xsl:for-each select="$root"> 
     <xsl:variable name="existing-element" select="key('cfg', $name)"/> 
     <xsl:element name="{$name}"> 
      <xsl:attribute name="attr"> 
       <xsl:choose> 
        <xsl:when test="$existing-element"> 
         <xsl:value-of select="$existing-element/@attr"/> 
        </xsl:when> 
        <xsl:otherwise> 
         <xsl:value-of select="$default-value"/> 
        </xsl:otherwise> 
       </xsl:choose> 
      </xsl:attribute> 
     </xsl:element> 
    </xsl:for-each> 
</xsl:template> 

</xsl:stylesheet> 
+0

выбрал ваш ответ, потому что это xslt 1.0 и гибкий :) –

2

Вот таблицы стилей XSLT:

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

<xsl:output indent="yes"/> 

<xsl:key name="cfg-els-by-name" match="config/*" use="node-name(.)"/> 

<xsl:param name="default"> 
    <config> 
    <element1 attr="a"/> 
    <element2 attr="b"/> 
    <element3 attr="c"/> 
    </config> 
</xsl:param> 

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

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

<xsl:template match="root[not(config)]"> 
    <xsl:copy> 
    <xsl:copy-of select="$default/config"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="root/config"> 
    <xsl:copy> 
    <xsl:apply-templates select="* , $default/config/*[not(key('cfg-els-by-name', node-name(.), $main-doc))]"> 
     <xsl:sort select="local-name(.)"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

Я не уверен, является ли порядок config дочернего элемента определяется сортировкой по local-name().

2

Вы хотите преобразование почти идентичности с несколькими модификациями.

Первый в английском-как псевдо-код:

  • в шаблоне для root, обрабатывать отдельно два случая: если у вас есть config элемент, процесс его; в противном случае поставьте один.
  • в шаблоне для 'config', для каждого потенциального дочернего элемента, обрабатывать два случая отдельно: если у вас есть такой элемент, обработайте его, в противном случае поставьте такой элемент.

Теперь, в XSLT-как псевдо-код:

В шаблоне для root, обрабатывать отдельно два случая:

<xsl:template match='root'> 
    <xsl:copy> 
    <xsl:choose> 
     <xsl:when test="config"> 
     <xsl:apply-templates/> 
     </ 
     <xsl:otherwise> 
     <config> 
      <element1 attr="a"/> 
      <element2 attr="b"/> 
      <element3 attr="c"/> 
     </config> 
     </ 
    </ 
    </ 
</ 

В шаблоне для config, поставки недостающих элементов по мере необходимости ,Если у вас есть фиксированный порядок, код может просто пройти через них по порядку:

<xsl:template match='config'> 
    <xsl:copy> 
    <xsl:choose> 
     <xsl:when test="element1"> 
     <xsl:apply-templates select="element1"/> 
     </ 
     <xsl:otherwise> 
     <element1 attr="a"/> 
     </ 
    </ 
    <xsl:choose> 
     <xsl:when test="element2"> 
     <xsl:apply-templates select="element2"/> 
     </ 
     <xsl:otherwise> 
     <elementb attr="b"/> 
     </ 
    </ 
    <!--* more chooses, as needed ... *--> 
    <xsl:choose> 
     <xsl:when test="elementN"> 
     <xsl:apply-templates select="elementN"/> 
     </ 
     <xsl:otherwise> 
     <elementN attr="N"/> 
     </ 
    </ 
    </ 
</ 

Если заказ ничем не ограничен, это может быть немного проще:

<xsl:template match="config"> 
    <xsl:copy> 
    <xsl:apply-templates/> 
    <xsl:if test="not(element1)"> 
     <element1 attr="a"/> 
    </ 
    <xsl:if test="not(element2)"> 
     <element2 attr="b"/> 
    </ 
    <!--* etc ... *--> 
    </ 
</ 

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

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