views:

85

answers:

2

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

+1  A: 

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...

ircmaxell
It is the opening and closing of a CDATA tag that is giving me the issue... Please refer to the original post.
ThinkingInBits
@ThinkingInBits: See edit
ircmaxell
This was the right answer as well, just didn't understand it at the time. Voted up though, thanks.
ThinkingInBits
+3  A: 

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>
VolkerK
THANK YOU. This is the information I needed. I didn't realize I could use both interchangeably.
ThinkingInBits
Will setting $b = dom_import_simplexml($b) mess up the iteration?
ThinkingInBits
It works, but I don't 100% understand why :P
ThinkingInBits
"Will setting $b= ... mess up the iteration?" - Obviously not ;-) But use another variable if you like. I just like to have as few variables as possible (within reason, e.g. $cdata could be left out as well) and since $b serves no other purpose I re-used it.
VolkerK