Antonio Pérez
Antonio Pérez

Reputation: 6982

Add a processing instruction to an existing XML document using libxml++

I'm trying to add a processing instruction node to an existing XML document to have a XSL transformation applied to the document when displayed in a browser. I looked up how to do it using libxml++ classes but couldn't find it out, so I tried using libxml2. This is what I came up with:

xmlpp::Document* Doc = Parser->get_document();

// Set processing instruction for stylesheet
const xmlNodePtr PINode = xmlNewDocPI(
    Doc->cobj(),
    reinterpret_cast<xmlChar*>("xml-stylesheet"),
    reinterpret_cast<xmlChar*>("href=\"../stylesheet.xslt\" type=\"text/xsl\"")
);

if (PINode == NULL) {
     // Never get here
}

Doc->write_to_file_formatted("mydoc.xml", "utf-8");

The processing instruction node is not written into the document. So what am I missing here?

Upvotes: 2

Views: 1273

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

Adding a processing instruction to an XML document can be done in pure XSLT:

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

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

 <xsl:template match="/">
  <xsl:processing-instruction name="xml-stylesheet">
    <xsl:text>href="../stylesheet.xslt" type="text/xsl"</xsl:text>
  </xsl:processing-instruction>
  <xsl:text>&#xA;</xsl:text>
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied to any XML document, such as the one below:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

the wanted result is produced:

<?xml-stylesheet href="../stylesheet.xslt" type="text/xsl"?>
<nums>
   <num>01</num>
   <num>02</num>
   <num>03</num>
   <num>04</num>
   <num>05</num>
   <num>06</num>
   <num>07</num>
   <num>08</num>
   <num>09</num>
   <num>10</num>
</nums>

Upvotes: 1

Antonio P&#233;rez
Antonio P&#233;rez

Reputation: 6982

It turns out that just calling xmlNewDocPI is not enough. It creates the processing instruction node and somehow associates it to the document but it doesn't actually attach it to the document.

For that purpose some of the xmlAdd* functions must be called and, since I need the PI to be included right below the XML declaration and not nested within the documents root node, I had to use the following:

xmlAddPrevSibling(Doc->get_root_node()->cobj(), PINode);

It looks kind of hackish but works. So the full snippet for the working code gets like this:

xmlpp::Document* Doc = Parser->get_document();

// Set processing instruction for stylesheet
const xmlNodePtr PINode = xmlNewDocPI(
    Doc->cobj(),
    reinterpret_cast<xmlChar*>("xml-stylesheet"),
    reinterpret_cast<xmlChar*>("href=\"../stylesheet.xslt\" type=\"text/xsl\"")
);

if (PINode != NULL) {
    xmlAddPrevSibling(Doc->get_root_node()->cobj(), PINode);
}

Doc->write_to_file_formatted("mydoc.xml", "utf-8");

Upvotes: 3

Related Questions