Question

I'm using boost serialization for persistence, and since the library doesn't have support for the idea of saving to an older version of the archive/data structure, I though I'd give XSLT & XPath a shot at transforming from a new version to an older version as needed.

I've gotten about half way there, but can't seem to finish it up ( this is also my first venture into XSLT & XPath/XQuery, so please forgive any obvious mistakes ).

Here's my starting XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="7">
<tester class_id="0" tracking_level="0" version="0">
    <count>2</count>
    <item_version>0</item_version>
    <item class_id="2" class_name="CLASS_D" tracking_level="0" version="0">
        <A class_id="1" tracking_level="1" version="0" object_id="_0">
            <pimpl class_id="3" tracking_level="1" version="0" object_id="_1">
                <b>1</b>
            </pimpl>
        </A>
        <pimpl class_id="4" tracking_level="1" version="0" object_id="_2">
            <c>2</c>
        </pimpl>
    </item>
    <item class_id="5" class_name="CLASS_E" tracking_level="0" version="0">
        <A object_id="_3">
            <pimpl class_id_reference="3" object_id="_4">
                <b>1</b>
            </pimpl>
        </A>
        <pimpl class_id="6" tracking_level="1" version="0" object_id="_5">
            <f>2</f>
        </pimpl>
    </item>
</tester>
</boost_serialization>

What I want to do, is transform the item with the attribute class_name="CLASS_E" to be like the item with class_name="CLASS_D" but I need to leave the object_id attribute alone.

This is what I want:

<?xml version="1.0" encoding="utf-8"?>
<boost_serialization signature="serialization::archive" version="7">
  <tester class_id="0" tracking_level="0" version="0">
    <count>2</count>
    <item_version>0</item_version>
    <item class_id="2" class_name="CLASS_D" tracking_level="0" version="0">
      <A class_id="1" tracking_level="1" version="0" object_id="_0">
        <pimpl class_id="3" tracking_level="1" version="0" object_id="_1">
          <b>1</b>
        </pimpl>
      </A>
      <pimpl class_id="4" tracking_level="1" version="0" object_id="_2">
        <c>2</c>
      </pimpl>
    </item>
    <item class_name="CLASS_D" class_id="2" tracking_level="0" version="0">
      <A object_id="_3">
        <pimpl class_id_reference="3" object_id="_4">
          <b>1</b>
        </pimpl>
      </A>
      <pimpl class_id="4" tracking_level="1" version="0" object_id="_5">
        <c>2</c>
      </pimpl>
    </item>
  </tester>
</boost_serialization>

This is the template as I have so far:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" omit-xml-declaration="no" encoding="UTF-8" indent="yes"/>

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

  <!-- replace attribute class_name value with another-->
  <!-- replace attribute class_id value with another-->
  <!-- only on this node!-->
  <!-- could call another template to change more nested things-->
  <xsl:template match="item/@class_name[. =  'CLASS_E']">
    <xsl:attribute name="class_name">CLASS_D</xsl:attribute>
    <xsl:attribute name="class_id">2</xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

I'm not sure how to continue editing the child node of the item I've matched with this line: as I need to change the "f" node to "c" and change the pimpl "class_id" from 6 to 4

Thanks in advance

Was it helpful?

Solution

The item you have just matched is an attribute, and so has no child nodes! The parent node will have been matched by the identity template, and so to 'continue' editing, just have a template matching the class_id attribute you wish to match, but include a match on the relevant item in the xpath expression.

For example, to change the class_id of the pimpl element, add this template

 <xsl:template match="item[@class_name =  'CLASS_E']/pimpl/@class_id">
    <xsl:attribute name="class_id">4</xsl:attribute>
 </xsl:template>

And to change the f element, add this template

<xsl:template match="item[@class_name =  'CLASS_E']/pimpl/f">
   <c>
      <xsl:apply-templates select="@*|node()"/>
   </c>
</xsl:template>

Remember, the match applies to the input document, so it doesn't matter that in the output document you have changed 'CLASS_E' to 'CLASS_D'.

Note that you may have an issue with your current template that matches the class_name attribute. In this you are replacing it with two attributes, including the class_id attribute which already exists. XSLT will replace attribute in the output tree where one with the same name has already been output. This means if your XML actually looked like this...

<item class_name="CLASS_E" class_id="5"  tracking_level="0" version="0">

Then it would be output would actually look this this

<item class_name="CLASS_D" class_id="5" tracking_level="0" version="0">

This is because the identity template will match the class_id attribute after your template that matches the class_name and so will replace the class_id attribute which the one it has currently matched.

But to cut a long story short, try this XSLT.....

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

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

  <xsl:template match="item/@class_name[. =  'CLASS_E']">
    <xsl:attribute name="class_name">CLASS_D</xsl:attribute>
  </xsl:template>

  <xsl:template match="item[@class_name =  'CLASS_E']/@class_id">
    <xsl:attribute name="class_id">2</xsl:attribute>
  </xsl:template>

  <xsl:template match="item[@class_name =  'CLASS_E']/pimpl/@class_id">
     <xsl:attribute name="class_id">4</xsl:attribute>
  </xsl:template>

  <xsl:template match="item[@class_name =  'CLASS_E']/pimpl/f">
     <c>
        <xsl:apply-templates select="@*|node()"/>
     </c>
  </xsl:template>
</xsl:stylesheet>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top