简体   繁体   English

在 xml 数据中使用 xslt append 新元素

[英]Using xslt append new element in xml data

I am trying to add a new element to my XML file data as below:我正在尝试向我的 XML 文件数据添加一个新元素,如下所示:

Orignal :原件

<storage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         version="1.0" ssd-storage="hybrid">
     <server port="6890" />
     <data-store name="mystore1" limit="700">
          <data-dir>${SSD_STORE_BASE}/myFolder1</data-dir>
     </data-store>
     <data-store name="mystore2" limit="700">
          <data-dir>${HDD_STORE_BASE}/myFolder2</data-dir>
     </data-store>
     <data-store name="mystore3" limit="700">
          <data-dir>${SSD_STORE_BASE}/myFolder3</data-dir>
     </data-store>
     .... many such data-stores for both HDD and SSD
</storage>

Expected预期的

<storage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         version="1.0" ssd-storage="hybrid">
     <server port="6890" />
     <data-store name="mystore1" limit="700">
          <data-dir>${SSD_STORE_BASE1}/myFolder1</data-dir>
          <data-dir>${SSD_STORE_BASE2}/myFolder1</data-dir>
     </data-store>
     <data-store name="mystore2" limit="700">
          <data-dir>${HDD_STORE_BASE1}/myFolder2</data-dir>
          <data-dir>${HDD_STORE_BASE2}/myFolder2</data-dir>
     </data-store>
     <data-store name="mystore3" limit="700">
          <data-dir>${SSD_STORE_BASE1}/myFolder3</data-dir>
          <data-dir>${SSD_STORE_BASE2}/myFolder3</data-dir>
     </data-store>
     .... many such data-stores for both HDD and SSD
</storage>

I want to pass the replacement parameters for SSD_STORE_BASE and HDD_STORE_BASE, and the XSLT will need to append a copy of "data-dir" element with a replaced value of the text in the element.我想传递 SSD_STORE_BASE 和 HDD_STORE_BASE 的替换参数,而 XSLT 将需要 append 的“data-dir”元素的副本,其中包含元素中文本的替换值。 How do I do this?我该怎么做呢? If I can use xsltproc to do this, that would be perfect.如果我可以使用 xsltproc 来做到这一点,那将是完美的。

You can achieve that with the following XSLT-1.0 stylesheet which can be processed by xsltproc with parameters.您可以使用以下 XSLT-1.0 样式表来实现这一点,该样式表可由xsltproc使用参数进行处理。 Set the parameters with设置参数

xsltproc --stringparam replacementSSD "newSSDValue" --stringparam replacementHDD "newHDDValue" transform.xslt input.xml

This can be transform.xslt :这可以是transform.xslt

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:param name="replacementSSD" select="'SSD_STORE_BASE'" />
  <xsl:param name="replacementHDD" select="'HDD_STORE_BASE'" />
    
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template> 
    
    <xsl:template match="data-dir[contains(.,'SSD_STORE_BASE')]">
      <xsl:copy>
        <xsl:value-of select="concat(substring-before(.,'SSD_STORE_BASE'),$replacementSSD,'1',substring-after(.,'SSD_STORE_BASE'))" />
      </xsl:copy>
      <xsl:text>&#xa;</xsl:text>
      <xsl:copy>
        <xsl:value-of select="concat(substring-before(.,'SSD_STORE_BASE'),$replacementSSD,'2',substring-after(.,'SSD_STORE_BASE'))" />
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="data-dir[contains(.,'HDD_STORE_BASE')]">
      <xsl:copy>
        <xsl:value-of select="concat(substring-before(.,'HDD_STORE_BASE'),$replacementHDD,'1',substring-after(.,'HDD_STORE_BASE'))" />
      </xsl:copy>
      <xsl:copy>
        <xsl:value-of select="concat(substring-before(.,'SSD_STORE_BASE'),$replacementHDD,'2',substring-after(.,'HDD_STORE_BASE'))" />
      </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>

This stylesheet could be optimized by merging the two templates, but I left them separately, because they may contain different functionality.这个样式表可以通过合并两个模板来优化,但是我把它们分开了,因为它们可能包含不同的功能。


An updated answer reflecting the new requirements:反映新要求的更新答案:

Counting is a bit complicated in XSLT-1.0, so I created a data-island that takes care of that - named cnt:counter .在 XSLT-1.0 中计数有点复杂,所以我创建了一个数据岛来处理这个问题 - 命名为cnt:counter In this example it counts up to three.在此示例中,它最多计数为 3。 Then, in the code, it iterates over this element to count up to three - the main string is preserved in a variable.然后,在代码中,它迭代这个元素以计数到三个 - 主字符串保存在一个变量中。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cache="geode.apache.org/schema/cache" xmlns:cnt="http://count.com">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:param name="replacementSSD" select="'SSD_STORE_BASE'" />
  <xsl:param name="replacementHDD" select="'HDD_STORE_BASE'" />
    
  <cnt:counter>
    <cnt:cnt>1</cnt:cnt>
    <cnt:cnt>2</cnt:cnt>
    <cnt:cnt>3</cnt:cnt>
  </cnt:counter>
    
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template> 
    
    <xsl:template match="cache:data-dir[contains(.,'SSD_STORE_BASE')]">
      <xsl:variable name="curStr" select="." />
      <xsl:for-each select="document('')/xsl:stylesheet/cnt:counter/cnt:cnt">
        <xsl:element name="data-dir" namespace="geode.apache.org/schema/cache">
            <xsl:value-of select="concat(substring-before($curStr,'SSD_STORE_BASE'),$replacementSSD,.,substring-after($curStr,'SSD_STORE_BASE'))" />
        </xsl:element>
        <xsl:text>&#xa;</xsl:text>
      </xsl:for-each>
    </xsl:template>
    
    <xsl:template match="cache:data-dir[contains(.,'HDD_STORE_BASE')]">
      <xsl:variable name="curStr" select="." />
      <xsl:for-each select="document('')/xsl:stylesheet/cnt:counter/cnt:cnt">
        <xsl:element name="data-dir" namespace="geode.apache.org/schema/cache">
            <xsl:value-of select="concat(substring-before($curStr,'HDD_STORE_BASE'),$replacementHDD,.,substring-after($curStr,'HDD_STORE_BASE'))" />
        </xsl:element>
        <xsl:text>&#xa;</xsl:text>
      </xsl:for-each>
    </xsl:template>
    
</xsl:stylesheet>

Its output is:其 output 为:

<storage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="geode.apache.org/schema/cache" version="1.0" ssd-storage="hybrid">
     <server port="6890"/>
     <data-store name="mystore1" limit="700">
          <data-dir>${SSD_STORE_BASE1}/myFolder1</data-dir>
          <data-dir>${SSD_STORE_BASE2}/myFolder1</data-dir>
          <data-dir>${SSD_STORE_BASE3}/myFolder1</data-dir>
     </data-store>
     <data-store name="mystore2" limit="700">
          <data-dir>${HDD_STORE_BASE1}/myFolder2</data-dir>
          <data-dir>${HDD_STORE_BASE2}/myFolder2</data-dir>
          <data-dir>${HDD_STORE_BASE3}/myFolder2</data-dir>
     </data-store>
     <data-store name="mystore3" limit="700">
          <data-dir>${SSD_STORE_BASE1}/myFolder3</data-dir>
          <data-dir>${SSD_STORE_BASE2}/myFolder3</data-dir>
          <data-dir>${SSD_STORE_BASE3}/myFolder3</data-dir>
     </data-store>
     .... many such data-stores for both HDD and SSD
</storage>

This solution does handle the newly introduced namespace and the counting in XSLT-1.0.这个解决方案确实处理了新引入的命名空间和 XSLT-1.0 中的计数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM