使用 xslt 在输出 xml 中插入元素

发布于 2024-11-01 20:14:41 字数 298 浏览 1 评论 0原文

我有以下 xml 文档:

<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
 </config>

我需要检查名称为“prop3”、“prop4”、“prop5”的属性是否存在,如果不存在,我需要将它们添加到配置下并保留现有元素。具有这些属性的元素可能存在,在这种情况下,我需要更改它们的值。有人可以帮忙吗?

i have the following xml document:

<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
 </config>

I need to check if properties with names "prop3", "prop4", "prop5" exist and if they don't, I need to add them under config keeping the existing elements. The elements with these properties may exist in which case, I need to change their values. Can someone help, please?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦里南柯 2024-11-08 20:14:41

这个简短的转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:replacements>
    <property name="prop3">Newval 3</property>
    <property name="prop4">Newval 4</property>
    <property name="prop5">Newval 5</property>
 </my:replacements>

 <xsl:variable name="vReps" select=
  "document('')/*/my:replacements/*"/>

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

 <xsl:template match=
  "property
    [@name
    =
     document('')/*/my:replacements/*/@name
    ]
  "/>

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

当应用于提供的 XML 文档时:

<config>
    <property name="prop1">val 1</property>
    <property name="prop2">val 2</property>
</config>

产生完全想要的正确结果:

<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
  <property name="prop3" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 3</property>
  <property name="prop4" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 4</property>
  <property name="prop5" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 5</property>
</config>

解释:

  1. 身份规则/模板“按原样”复制每个节点。

  2. 任何具有 name 属性的 property 元素的重写模板,该属性具有 my: 的 name 属性之一的值: replacements/property 元素,实际上会从文档中删除任何此类元素(因为模板的正文为空。

  3. 与文档顶部元素匹配的覆盖模板会浅复制它,然后复制其属性并将模板应用到其所有后代节点(实际上复制除上面 2. 中的那些之外的所有节点)。最后,它复制所有替换元素是嵌入式的孩子my:replacements 元素。

  4. 当这些替换元素位于单独的 XML 文档中(而不是像现在仅为方便起见那样位于 XSLT 样式表中)时,新复制的替换元素中出现的命名空间节点将消失。

请注意:此解决方案的主要思想是在元素存在时替换元素或在元素不存在时添加元素 - 这与复制新元素(不检查是否存在)相同覆盖任何现有元素。

This short and simple transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:replacements>
    <property name="prop3">Newval 3</property>
    <property name="prop4">Newval 4</property>
    <property name="prop5">Newval 5</property>
 </my:replacements>

 <xsl:variable name="vReps" select=
  "document('')/*/my:replacements/*"/>

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

 <xsl:template match=
  "property
    [@name
    =
     document('')/*/my:replacements/*/@name
    ]
  "/>

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

when applied on the provided XML document:

<config>
    <property name="prop1">val 1</property>
    <property name="prop2">val 2</property>
</config>

produces exactly the wanted, correct result:

<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
  <property name="prop3" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 3</property>
  <property name="prop4" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 4</property>
  <property name="prop5" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 5</property>
</config>

Explanation:

  1. The identity rule/template copies every node "as-is".

  2. The overriding template for any property element with name attribute having the value of one of the name attributes of the my:replacements/property elements, in effects deletes any such element from the document (because of the empty body of the template.

  3. The overriding template that matches the top element of the document shallow-copies it, then copies its attributes and applies templates to all of its descendent nodes (in effect copying all of them except those in 2. above. Finally, it copies all replacement elements that are children of the embedded my:replacements element.

  4. The namespace nodes that appear in the newly copied replacement elements will disappear when these replacement elements are in a separate XML document (not in the XSLT stylesheet as is now done solely for convenience).

Do note: The main idea of this solution is that replacing elements when they exist or adding elements when they don't exist -- this is just the same as (without checking for existence) copying the new elements overwriting any existing elements.

橪书 2024-11-08 20:14:41

下面是此类功能的示例 xsl 样式表:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/">
        <config>
            <xsl:for-each select="config/property">
                <!-- Fills up from 1 until first prop -->
                <xsl:if test="string(number(substring(preceding-sibling::property/@name,5,1) + 1)) = 'NaN' and number(substring(@name,5,1)) > 1">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="1"/>
                        <xsl:with-param name="until" select="number(substring(@name,5,1))"/>
                    </xsl:call-template>
                </xsl:if>
                <!-- Fills up the gaps -->
                <xsl:if test="number(substring(@name,5,1)) > number(substring(preceding-sibling::property/@name,5,1) + 1)">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="number(substring(preceding-sibling::property/@name,5,1) + 1)"/>
                        <xsl:with-param name="until" select="number(substring(@name,5,1))"/>
                    </xsl:call-template>
                </xsl:if>
                <!-- copies the current node -->
                <property>
                    <xsl:attribute name="name">
                        <xsl:value-of select="@name"/>
                    </xsl:attribute>
                    <xsl:value-of select="text()"/>
                </property>
                <!-- Fills up to 5 from last prop -->
                <xsl:if test="substring(following-sibling::property/@name, 1, 4) != 'prop'">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="number(substring(@name,5,1)) + 1"/>
                        <xsl:with-param name="until" select="6"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:for-each>
        </config>
    </xsl:template>
    <xsl:template name="filler" match="property">
        <xsl:param name="from"/>
        <xsl:param name="until"/>
        <xsl:choose>
            <xsl:when test="$from < $until">
                <property>
                    <xsl:attribute name="name">
                        <xsl:value-of select="concat('prop', $from)"/>
                    </xsl:attribute>
                    <xsl:value-of select="concat('val ', $from)"/>
                </property>
                <xsl:call-template name="filler">
                    <xsl:with-param name="from" select="$from + 1" />
                    <xsl:with-param name="until" select="$until"/>
                </xsl:call-template>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

这段代码基本上填充了 xml,以确保它包含从 prop1 到 prop5 的所有值。

不是那么复杂 - 因为我不知道确切的目的 - 但也许它会帮助你,并且你可以根据你的需要概括它。

这样,输出将

<?xml version='1.0' encoding='UTF-8' ?>
<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
  <property name="prop3">val 3</property>
  <property name="prop4">val 4</property>
  <property name="prop5">val 5</property>
</config>

针对以下任何输入:

<config>
    <property name="prop1">val 1</property>
    <property name="prop2">val 2</property>
</config>

<config>
    <property name="prop2">val 2</property>
</config>

<config>
    <property name="prop1">val 1</property>
    <property name="prop4">val 4</property>
</config>

Here is a sample xsl stylesheet for such a functionality:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/">
        <config>
            <xsl:for-each select="config/property">
                <!-- Fills up from 1 until first prop -->
                <xsl:if test="string(number(substring(preceding-sibling::property/@name,5,1) + 1)) = 'NaN' and number(substring(@name,5,1)) > 1">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="1"/>
                        <xsl:with-param name="until" select="number(substring(@name,5,1))"/>
                    </xsl:call-template>
                </xsl:if>
                <!-- Fills up the gaps -->
                <xsl:if test="number(substring(@name,5,1)) > number(substring(preceding-sibling::property/@name,5,1) + 1)">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="number(substring(preceding-sibling::property/@name,5,1) + 1)"/>
                        <xsl:with-param name="until" select="number(substring(@name,5,1))"/>
                    </xsl:call-template>
                </xsl:if>
                <!-- copies the current node -->
                <property>
                    <xsl:attribute name="name">
                        <xsl:value-of select="@name"/>
                    </xsl:attribute>
                    <xsl:value-of select="text()"/>
                </property>
                <!-- Fills up to 5 from last prop -->
                <xsl:if test="substring(following-sibling::property/@name, 1, 4) != 'prop'">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="number(substring(@name,5,1)) + 1"/>
                        <xsl:with-param name="until" select="6"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:for-each>
        </config>
    </xsl:template>
    <xsl:template name="filler" match="property">
        <xsl:param name="from"/>
        <xsl:param name="until"/>
        <xsl:choose>
            <xsl:when test="$from < $until">
                <property>
                    <xsl:attribute name="name">
                        <xsl:value-of select="concat('prop', $from)"/>
                    </xsl:attribute>
                    <xsl:value-of select="concat('val ', $from)"/>
                </property>
                <xsl:call-template name="filler">
                    <xsl:with-param name="from" select="$from + 1" />
                    <xsl:with-param name="until" select="$until"/>
                </xsl:call-template>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

This piece of code basically fills up the xml to make sure that it contains all the values from prop1 to prop5.

Not that sophisticated -since i don't know the exact purpose-, but maybe it will help you out, and you can generalize it for your needs.

This way the output will be

<?xml version='1.0' encoding='UTF-8' ?>
<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
  <property name="prop3">val 3</property>
  <property name="prop4">val 4</property>
  <property name="prop5">val 5</property>
</config>

for any of the following inputs:

<config>
    <property name="prop1">val 1</property>
    <property name="prop2">val 2</property>
</config>

<config>
    <property name="prop2">val 2</property>
</config>

<config>
    <property name="prop1">val 1</property>
    <property name="prop4">val 4</property>
</config>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文