Diogo Almeida
Diogo Almeida

Reputation: 124

How to use xquery to find node and addChild to it?

Is it possible to use xpath/xquery to query for a specific xml node, and then import/add a child node to it?

Example (code taken from http://codepad.org/gJ1Y2LjM , that was presented in similar question, but not the same):

1. I want to add an array

$book = array('isbn'=>123456789099, 'title' => 'Harry Potter 3', 'author' => 'J K. Rowling', 'edition' => '2007');

2. To an existing XML.

$doc = new DOMDocument();
$doc->loadXML('<?xml version="1.0"?>
<books>
    <book>
        <isbn>123456789098</isbn>
        <title>Harry Potter</title>
        <author>J K. Rowling</author>
        <edition>2005</edition>
    </book>
    <book>
        <placeItHere></placeItHere>
        <isbn>1</isbn>
        <title>stuffs</title>
        <author>DA</author>
        <edition>2014</edition>
    </book>
</books>');

3. To do that it is used the following fragment.

$fragment = $doc->createDocumentFragment();
$fragment->appendXML("    <book>
<isbn>{$book['isbn']}</isbn>
        <title>{$book['title']}</title>
        <author>{$book['author']}</author>
        <edition>{$book['edition']}</edition>
    </book>
");

4. But instead of appending it to the root node, has almost every example i found on internet:

$doc->documentElement->appendChild($fragment);

5. I want to append it (p.ex) to the node found in /books/book/placeItHere and not using getElementbyId or tagName, but a xpath/xquery. I tryed

$xp = new domxpath($doc);
$parent = $xp->query("books/book/placeItHere");

to reach the node, but never managed to use it as the parent.

Question: How to use that location to appendChild $fragment? Is it possible?

**YOUR BEAUTIFUL MAGIC**

In the end I will just save it.

echo $doc->saveXML();

Thank you for any help you can give to me.

Upvotes: 2

Views: 671

Answers (1)

user3942918
user3942918

Reputation: 26375

A couple problems:

  1. Your xpath expression should be /books/book/placeItHere (with the leading /).
  2. DOMXPath::query() returns a DOMNodeList rather than a DOMNode, so you'll need to grab your item() from it.

I rarely recommend using document fragments as playing fast and loose with raw XML regularly leads to problems. For example if your book title included an ampersand appendXML() would crap out with a parser error.

Instead I suggest createElement() and createTextNode(), which will handle transforming things like & into &amp; automatically.


Example:

$xml = <<<'XML'
<?xml version="1.0"?>
<books>
    <book>
        <isbn>123456789098</isbn>
        <title>Harry Potter</title>
        <author>J K. Rowling</author>
        <edition>2005</edition>
    </book>
    <book>
        <placeItHere></placeItHere>
        <isbn>1</isbn>
        <title>stuffs</title>
        <author>DA</author>
        <edition>2014</edition>
    </book>
</books>
XML;

$book = [
    'isbn'    => 123456789099,
    'title'   => 'Harry Potter 3',
    'author'  => 'J K. Rowling',
    'edition' => '2007'
];

$dom = new DOMDocument();
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);

$placeItHere = $xpath->query('/books/book/placeItHere')->item(0);
$newBook = $placeItHere->appendChild($dom->createElement('book'));
foreach ($book as $part => $value) {
    $element = $newBook->appendChild($dom->createElement($part));
    $element->appendChild($dom->createTextNode($value));
}

echo $dom->saveXML();

Output:

<?xml version="1.0"?>
<books>
  <book>
    <isbn>123456789098</isbn>
    <title>Harry Potter</title>
    <author>J K. Rowling</author>
    <edition>2005</edition>
  </book>
  <book>
    <placeItHere>
      <book>
        <isbn>123456789099</isbn>
        <title>Harry Potter 3</title>
        <author>J K. Rowling</author>
        <edition>2007</edition>
      </book>
    </placeItHere>
    <isbn>1</isbn>
    <title>stuffs</title>
    <author>DA</author>
    <edition>2014</edition>
  </book>
</books>

Upvotes: 1

Related Questions