Asked  7 Months ago    Answers:  5   Viewed   82 times

say I have XML:

<root>
  <nodeA />
  <nodeA />
  <nodeA />
  <nodeC />
  <nodeC />
  <nodeC />
</root>

How do I insert "nodeB" between As and Cs? In PHP, preferably via SimpleXML? Like:

<root>
  <nodeA />
  <nodeA />
  <nodeA />
  <nodeB />
  <nodeC />
  <nodeC />
  <nodeC />
</root>

 Answers

72

The following is a function to insert a new SimpleXMLElement after some other SimpleXMLElement. Since this isn't directly possible with SimpleXML, it uses some DOM classes/methods behind-the-scenes to get the job done.

function simplexml_insert_after(SimpleXMLElement $insert, SimpleXMLElement $target)
{
    $target_dom = dom_import_simplexml($target);
    $insert_dom = $target_dom->ownerDocument->importNode(dom_import_simplexml($insert), true);
    if ($target_dom->nextSibling) {
        return $target_dom->parentNode->insertBefore($insert_dom, $target_dom->nextSibling);
    } else {
        return $target_dom->parentNode->appendChild($insert_dom);
    }
}

And an example of how it might be used (specific to your question):

$sxe = new SimpleXMLElement('<root><nodeA/><nodeA/><nodeA/><nodeC/><nodeC/><nodeC/></root>');
// New element to be inserted
$insert = new SimpleXMLElement("<nodeB/>");
// Get the last nodeA element
$target = current($sxe->xpath('//nodeA[last()]'));
// Insert the new element after the last nodeA
simplexml_insert_after($insert, $target);
// Peek at the new XML
echo $sxe->asXML();

If you want/need an explanation of how this works (the code is fairly simple but might include foreign concepts), just ask.

Wednesday, March 31, 2021
 
Lorav
answered 7 Months ago
28

From http://bugs.php.net/49660:

Since version 2.7.3 libxml limits the maximum size of a single text node to 10MB. The limit can be removed with a new option, XML_PARSE_HUGE. PHP has no way to specify this option to libxml.

So I imagine this flag is the way that PHP now has to specify this option.

Wednesday, March 31, 2021
 
muaaz
answered 7 Months ago
24

simplexml_load_string() (as the name suggest) load xml from a string and returns an object of SimepleXMLElement. There is no difference between this and using just the usual constructor of the class.

Update:

SimpleXML::__construct() has an additional parameter (the third one) bool $data_is_url = false. If this is true the behavior is the same as simplexml_load_file() (in conjunction with the common stream wrappers). Thus both simplexml_load_*()-functions cover the same functionality as SimpleXML::__construct().

Additional the functions simplexml_load_*() has a second parameter string $class_name = "SimpleXMLElement" to specify the class of the object to get returned. Thats not a specific feature of the functions, because you can "use" something very similar with usual instanciation too

class MyXML extends SimpleXMLElement {}
$a = new MyXML($xml);
$b = simplexml_load_string($xml, 'MyXML');

A difference between the OOP- and the procedural approach is, that the functions return false on error, but the constructor throws an Exception.

Saturday, May 29, 2021
 
rlanvin
answered 5 Months ago
72

The best way to make "pseudo-inplace" changes to a file in Python is with the fileinput module from the standard library:

import fileinput

processing_foo1s = False

for line in fileinput.input('1.txt', inplace=1):
  if line.startswith('foo1'):
    processing_foo1s = True
  else:
    if processing_foo1s:
      print 'foo bar'
    processing_foo1s = False
  print line,

You can also specify a backup extension if you want to keep the old version around, but this works in the same vein as your code -- uses .bak as the backup extension but also removes it once the change has successfully completed.

Besides using the right standard library module, this code uses simpler logic: to insert a "foo bar" line after every run of lines starting with foo1, a boolean is all you need (am I inside such a run or not?) and the bool in question can be set unconditionally just based on whether the current line starts that way or not. If the precise logic you desire is slightly different from this one (which is what I deduced from your code), it shouldn't be hard to tweak this code accordingly.

Monday, June 7, 2021
 
muaddhib
answered 5 Months ago
99

Actually your code should work - in order to add a sub node you just have to do:

myNode.Nodes.Add(new TreeNode("Sub node"));

Maybe the problem is in the way you refer to your existing nodes. I am guessing that tree.Nodes[item.Name] returned null?

In order for this indexer to find the node, you need to specify a key when you add the node. Did you specify the node name as a key? For example, the following code works for me:

treeView1.Nodes.Add("key", "root");
treeView1.Nodes["key"].Nodes.Add(new TreeNode("Sub node"));

If my answer doesn't work, can you add more details on what does happen? Did you get some exception or did simply nothing happen?

PS: in order to store an object in a node, instead of using the Tag property, you can also derive your own class from TreeNode and store anything in it. If you're developing a library, this is more useful because you are leaving the Tag property for your users to use.

Ran

Tuesday, October 19, 2021
 
Álvaro García
answered 1 Week ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :