【问题标题】:Adding XML nodes to an existing XML using XPATH使用 XPATH 将 XML 节点添加到现有 XML
【发布时间】:2022-01-06 10:37:28
【问题描述】:

我需要使用 XPATH 将新的 XML 节点添加到现有的 XML。

现有的 XML:

<create>
   <article>
      <identifier>Test</identifier>
   </article>
</create>

XSLT Used (Get if from another thread in stackoverflow):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vPop" as="element()*">
    <item path="/create/article[1]/id">1</item>
    <item path="/create/article[1]/description">bar</item>
    <item path="/create/article[1]/name[1]">foo</item>
    <item path="/create/article[1]/price[1]/amount">00.00</item>
    <item path="/create/article[1]/price[1]/currency">USD</item>
    <item path="/create/article[1]/price[2]/amount">11.11</item>
    <item path="/create/article[1]/price[2]/currency">AUD</item>
    <item path="/create/article[2]/id">2</item>
    <item path="/create/article[2]/description">some name</item>
    <item path="/create/article[2]/name[1]">some description</item>
    <item path="/create/article[2]/price[1]/amount">00.01</item>
    <item path="/create/article[2]/price[1]/currency">USD</item>
 </xsl:variable>

 <xsl:template match="/">
  <xsl:sequence select="my:subTree($vPop/@path/concat(.,'/',string(..)))"/>
 </xsl:template>

 <xsl:function name="my:subTree" as="node()*">
  <xsl:param name="pPaths" as="xs:string*"/>

  <xsl:for-each-group select="$pPaths"
    group-adjacent=
        "substring-before(substring-after(concat(., '/'), '/'), '/')">
    <xsl:if test="current-grouping-key()">
     <xsl:choose>
       <xsl:when test=
          "substring-after(current-group()[1], current-grouping-key())">
         <xsl:element name=
           "{substring-before(concat(current-grouping-key(), '['), '[')}">

          <xsl:sequence select=
            "my:subTree(for $s in current-group()
                         return
                            concat('/',substring-after(substring($s, 2),'/'))
                             )
            "/>
        </xsl:element>
       </xsl:when>
       <xsl:otherwise>
        <xsl:value-of select="current-grouping-key()"/>
       </xsl:otherwise>
     </xsl:choose>
     </xsl:if>
  </xsl:for-each-group>
 </xsl:function>
</xsl:stylesheet>

使用上面的 XSLT,XPATH 被转换为 XML 节点。但问题是现有的 XML 节点 (&lt;identifier&gt;Test&lt;/identifier&gt;) 丢失了。请帮助解决此问题。这是一个示例 XML,实际的 XML 包含许多现有的 XML 节点。

实际结果:

<create>
   <article>
      <id>1</id>
      <description>bar</description>
      <name>foo</name>
      <price>
         <amount>00.00</amount>
         <currency>USD</currency>
      </price>
      <price>
         <amount>11.11</amount>
         <currency>AUD</currency>
      </price>
   </article>
   <article>
      <id>2</id>
      <description>some name</description>
      <name>some description</name>
      <price>
         <amount>00.01</amount>
         <currency>USD</currency>
      </price>
   </article>
</create>

预期结果:

<create>
   <article>
      <identifier>Test</identifier>
      <id>1</id>
      <description>bar</description>
      <name>foo</name>
      <price>
         <amount>00.00</amount>
         <currency>USD</currency>
      </price>
      <price>
         <amount>11.11</amount>
         <currency>AUD</currency>
      </price>
   </article>
   <article>
      <id>2</id>
      <description>some name</description>
      <name>some description</name>
      <price>
         <amount>00.01</amount>
         <currency>USD</currency>
      </price>
   </article>
</create>

【问题讨论】:

  • from 创建元素 XPath 是一种奇怪的(并且从根本上被破坏的)生成 XML 的方式。这样做有什么意义?谁编写了您用来生成 XML 的 XPath?
  • 如果主输入具有例如,决定形成哪个值的规则是什么?一个元素 /create/article[1]/id 并且您的带有 XPaths 的变量也想添加它?
  • @Tomalak 理想情况下,要求是动态地将新字段添加到文档中。在开发期间,开发人员并不了解这些领域。因此,我们将这些字段作为 XPATH 存储在缓存中。在运行时,我们将为该文档创建一个基于 XML 的 XML,然后从存储在缓存中的 XPATH 创建这些新字段。
  • @MartinHonnen 如果主输入中已经存在相同的元素,则 XPATH 中的变量可以覆盖它。
  • 再次。这是一个从根本上被破坏的设计。它不聪明,没有必要,不可扩展,不可持续——这不是一个好主意。你真的应该重新工作这部分。如果您可以编写 XPath 来“存储”有关 XML 的结构信息,那么您也可以直接编写 XML。

标签: xml xslt xsd xslt-2.0 xslt-3.0


【解决方案1】:

这是基于现有函数创建新节点并将它们与输入文档方法合并的粗略概述:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  xmlns:my="http://example.com/my-functions"
  expand-text="yes">
  
<xsl:variable name="vPop" as="element()*">
    <item path="/create/article[1]/id">1</item>
    <item path="/create/article[1]/description">bar</item>
    <item path="/create/article[1]/name[1]">foo</item>
    <item path="/create/article[1]/price[1]/amount">00.00</item>
    <item path="/create/article[1]/price[1]/currency">USD</item>
    <item path="/create/article[1]/price[2]/amount">11.11</item>
    <item path="/create/article[1]/price[2]/currency">AUD</item>
    <item path="/create/article[2]/id">2</item>
    <item path="/create/article[2]/description">some name</item>
    <item path="/create/article[2]/name[1]">some description</item>
    <item path="/create/article[2]/price[1]/amount">00.01</item>
    <item path="/create/article[2]/price[1]/currency">USD</item>
 </xsl:variable>
 
 <xsl:variable name="new-nodes">
   <xsl:sequence select="my:subTree($vPop/@path/concat(.,'/',string(..)))"/>
 </xsl:variable>

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
    
  <xsl:template match="/" name="xsl:initial-template">
    <xsl:sequence select="my:merge(*, $new-nodes/*)"/>
  </xsl:template>


 <xsl:function name="my:merge" as="node()*">
   <xsl:param name="node1" as="node()*"/>
   <xsl:param name="node2" as="node()*"/>
   <xsl:for-each-group select="$node1, $node2" group-by="path()">
     <xsl:copy>
       <xsl:sequence select="my:merge(@*, current-group()[2]/@*)"/>
       <xsl:sequence select="my:merge(node(), current-group()[2]/node())"/>
     </xsl:copy>
   </xsl:for-each-group>
 </xsl:function>

 <xsl:function name="my:subTree" as="node()*">
   
   
  <xsl:param name="pPaths" as="xs:string*"/>

  <xsl:for-each-group select="$pPaths"
    group-adjacent=
        "substring-before(substring-after(concat(., '/'), '/'), '/')">
    <xsl:if test="current-grouping-key()">
     <xsl:choose>
       <xsl:when test=
          "substring-after(current-group()[1], current-grouping-key())">
         <xsl:element name=
           "{substring-before(concat(current-grouping-key(), '['), '[')}">

          <xsl:sequence select=
            "my:subTree(for $s in current-group()
                         return
                            concat('/',substring-after(substring($s, 2),'/'))
                             )
            "/>
        </xsl:element>
       </xsl:when>
       <xsl:otherwise>
        <xsl:value-of select="current-grouping-key()"/>
       </xsl:otherwise>
     </xsl:choose>
     </xsl:if>
  </xsl:for-each-group>
 </xsl:function>
</xsl:stylesheet>

但大多数合并需求比我在此处展示的简单 my:merge 复杂得多,因此您需要改进该功能。

【讨论】:

  • 这工作正常,从 XPATH 生成新的 XML 节点并与初始 XML 合并,但 XML 标记值没有显示。请您帮忙解决一下
    Test
  • 我确实在xsltfiddle.liberty-development.net/jy6KM7B获得了内容
  • 非常感谢。让我进一步检查并更新您。
  • 这工作正常。非常感谢。
猜你喜欢
  • 2017-01-23
  • 2020-10-28
  • 2012-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多