Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep DOMDocument from saving < as &lt

I'm using simpleXML to add in a child node within one of my XML documents... when I do a print_r on my simpleXML object, the < is still being displayed as a < in the view source. However, after I save this object back to XML using DOMDocument, the < is converted to &lt; and the > is converted to &gt;

Any ideas on how to change this behavior? I've tried adding dom->substituteEntities = false;, but this did no good.

    //Convert SimpleXML element to DOM and save
    $dom = new DOMDocument('1.0');
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = false;
    $dom->substituteEntities = false;
    $dom->loadXML($xml->asXML());
    $dom->save($filename);

Here is where I'm using the <:

$new_hint = '<![CDATA[' . $value[0] . ']]>';               
$PrintQuestion->content->multichoice->feedback->hint->Passage->Paragraph->addChild('TextFragment', $new_hint);

The problem, is I'm using simple XML to iterate through certain nodes in the XML document, and if an attribute matches a given ID, a specific child node is added with CDATA. Then after all processsing, I save the XML back to file using DOMDocument, which is where the < is converted to &lt, etc.

Here is a link to my entire class file, so you can get a better idea on what I'm trying to accomplish. Specifically refer to the hint_insert() method at the bottom.

http://pastie.org/1079562

like image 694
ThinkingInBits Avatar asked Aug 06 '10 13:08

ThinkingInBits


2 Answers

SimpleXML and php5's DOM module use the same internal representation of the document (facilitated by libxml). You can switch between both apis without having to re-parse the document via simplexml_import_dom() and dom_import_simplexml().
I.e. if you really want/have to perform the iteration with the SimpleXML api once you've found your element you can switch to the DOM api and create the CData section within the same document.

<?php
$doc = new SimpleXMLElement('<a>
  <b id="id1">a</b>
  <b id="id2">b</b>
  <b id="id3">c</b>
</a>');


foreach( $doc->xpath('b[@id="id2"]') as $b ) {
  $b = dom_import_simplexml($b);
  $cdata = $b->ownerDocument->createCDataSection('0<>1');
  $b->appendChild($cdata);
  unset($b);
}

echo $doc->asxml();

prints

<?xml version="1.0"?>
<a>
  <b id="id1">a</b>
  <b id="id2">b<![CDATA[0<>1]]></b>
  <b id="id3">c</b>
</a>
like image 166
VolkerK Avatar answered Nov 14 '22 02:11

VolkerK


The problem is that you're likely adding that as a string, instead of as an element.

So, instead of:

$simple->addChild('foo', '<something/>');

which will be treated as text:

$child = $simple->addChild('foo');
$child->addChild('something');

You can't have a literal < in the body of the XML document unless it's the opening of a tag.

Edit: After what you describe in the comments, I think you're after:

DomDocument::createCDatatSection()

$child = $dom->createCDataSection('your < cdata > body ');
$dom->appendChild($child);

Edit2: After reading your edit, there's only one thing I can say:

You're doing it wrong... You can't add elements as a string value for another element. Sorry, you just can't. That's why it's escaping things, because DOM and SimpleXML are there to make sure you always create valid XML. You need to create the element as an object... So, if you want to create the CDATA child, you'd have to do something like this:

$child = $PrintQuestion.....->addChild('TextFragment');
$domNode = dom_import_simplexml($child);
$cdata = $domNode->ownerDocument->createCDataSection($value[0]); 
$domNode->appendChild($cdata);

That's all there should be to it...

like image 34
ircmaxell Avatar answered Nov 14 '22 00:11

ircmaxell