2014-10-28 84 views
0

我需要使用簡單配置更新大量xml文件。我有這個問題 - config元素在我的xml文件中是可選的,它們可以有一些配置。XSLT:插入元素(如果它們不存在)

所以我想做的事:

  1. 插入<config/>所有預定義element的如果缺少
  2. 插入缺失elementconfig標籤。如果他們已經在那裏 - 就這樣離開。前

<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> 

所以我的想法是有幾個模板和應用第一步,如果它不存在,並在單獨的mode內做第二步。但它沒有解決。

UPD。 我使用xslt 1.0,但我猜測切換到2.0將不會有問題。

預定義的元素是:

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

你使用哪種版本的XSLT的?您需要添加哪些預定義的元素? – 2014-10-28 14:25:12

回答

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和靈活的:) – 2014-10-28 17:02:23

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 ... *--> 
    </ 
</ 

除非你正在做的事情比你顯示的更復雜,我沒有看到這裏需要一個額外的模式。