简体   繁体   中英

XSLT to update an attribute given just the new value and XPath to the attribute

I have some code that compares two XML documents for attribute differences (updates only, not new attribute nodes) and generates a set of XPath pointers to the attributes and the new values for the attribute.

The Setup

For example, given an old XML and new xml:

Old XML

<EntityA>
  <EntityB id="foo1" value="bar1" ignoredbutsave="bazz1"/>
</EntityA>

New XML

<EntityA>
  <EntityB id="foo2" value="bar2"/>
</EntityA>

My code would return

/EntityA/EntityB/@id, foo2
/EntityA/EntityB/@value, bar2

I would like to generate an XSLT that merges the old XML into the new XML, to create the following XML:

<EntityA>
  <EntityB id="foo2" value="bar2" ignoredbutsave="bazz1"/>
</EntityA>

All answers I've found on SO assume some prior knowledge about the attribute name. In this case, I'm given only the XPath reference the attribute, not the name itself. I know I could parse the XPath string to derive the attribute name, but would prefer to keep that complexity out of the code.

What I've tried

I can't use an attribute value template because I need to copy the ignoredbutsave attribute from the old XML. I've tried to use an xsl:param to select the attribute name from the XPath and use it within an xsl:attribute, like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes"/>

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

  <xsl:template match="/EntityA/EntityB/@id">
    <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
       <xsl:param name="newValue" select="name(/EntityA/EntityB/@id)"/>
         <xsl:attribute name="$newValue">newAttributeId</xsl:attribute>
     </xsl:copy>
  </xsl:template>

  <xsl:template match="/EntityA/EntityB/@value">
    <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
       <xsl:param name="myattrname" select="name(/EntityA/EntityB/@value)"/>
         <xsl:attribute name="$myattrname">newAttributeValue</xsl:attribute>
     </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

However, this causes the error The value '$myattrname' of the attribute 'name' is not a valid QName.

So, the question is given an XPath for an attribute and a new value for that attribute, how do I generate an an XSLT that updates that value without explicitly referencing the attribute name?

This XSLT transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes"/>

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

  <xsl:template match="/EntityA/EntityB/@id">
    <xsl:attribute name="{name()}">foo2</xsl:attribute>
  </xsl:template>

  <xsl:template match="/EntityA/EntityB/@value">
    <xsl:attribute name="{name()}">bar2</xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

Applied to your old XML:

<EntityA>
  <EntityB id="foo1" value="bar1" ignoredbutsave="bazz1"/>
</EntityA>

Yields your old XML with the required attribute value substitutions made:

<EntityA>
  <EntityB id="foo2" value="bar2" ignoredbutsave="bazz1"/>
</EntityA>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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