LoadXMLData()
expects the input string to be a well-formed XML document. The solution I gave you for your previous question worked because you were specifying individual XML elements which by themselves can act be standalone documents. But a PCDATA element by itself is not a well-formed XML document. Try wrapping it in a fake element, eg:
tmpDoc := LoadXMLData('<Doc>' + MissingElements[j] + '</Doc>').DocumentElement;
for I := 0 to tmpDoc.ChildNodes.Count-1 do
tmpNode.ChildNodes[1].ChildNodes[0].ChildNodes.Add(tmpDoc.ChildNodes[I]);
Update: You are getting an "index out of bounds" error because you are not taking the whitespace DOM nodes into account when accessing the ChildNodes
.
Given the XML you have shown:
XMLStarting.Add('<?xml version="1.0" encoding="UTF-16" standalone="no"?>');
XMLStarting.Add('<Programs>');
XMLStarting.Add(' <Program_Group Batch_No="{12345678-1234-1234-1234-123456789ABC}" Description="FOO_824_1">');
XMLStarting.Add(' <Program Name="PROG_1">');
XMLStarting.Add(' <Class Name="CLASS_1">');
XMLStarting.Add(' <Property Name="DB" RttiType="tkString"> </Property>');
XMLStarting.Add(' <Property Name="SystemDate" RttiType="tkClass" ClassType="TXSDATE">12/30/1899</Property>');
XMLStarting.Add(' </Class>');
XMLStarting.Add(' </Program>');
XMLStarting.Add(' </Program_Group>');
XMLStarting.Add('</Programs>');
And given the code you have shown which is failing:
FragNode := storedXMLObj.DocumentElement.ChildNodes[0];
FragNode.ChildNodes.Nodes[0].ChildNodes.Nodes[0].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement.ChildNodes.Nodes[0]);
The following is true:
storedXMLObj.DocumentElement
refers to the<Programs>
node.- its
ChildNodes[0]
node refers to the whitespace between the<Programs>
and<Program_Group>
nodes, but you are expecting it to refer to the<Program_Group>
node instead. - thus,
FragNode.ChildNodes.Nodes[0]
fails because FragNode is a text-only node that has no children!
You can confirm that for yourself. FragNode.NodeName
is '#text'
, FragNode.NodeType
is ntText
, FragNode.NodeValue
is #$A' '
, FragNode.HasChildNodes
is False, and FragNode.IsTextElement
is True.
In other words, the above XML has the following structure to it:
ntElement 'Programs'
|
|_ ntText #$A' '
|
|_ ntElement 'Program_Group'
|
|_ ntText #$A' '
|
|_ ntElement 'Program'
| |
| |_ ntText #$A' '
| |
| |_ ntElement 'Class'
| | |
| | |_ ntText #$A' '
| | |
| | |_ nElement 'Property'
| | | |
| | | |_ ntText ' '
| | |
| | |_ ntText #$A' '
| | |
| | |_ ntElement 'Property'
| | | |
| | | |_ ntText '12/30/1899'
| | |
| | |_ ntText #$A' '
| |
| |_ ntText #$A' '
|
|_ ntText #$A' '
Hopefully that makes it a bit clearer.
So, to accomplish what you are attempting to do, you would need something more like this:
FragNode := storedXMLObj.DocumentElement.ChildNodes[1];
FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement);
FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement);
If you want to preserve whitespace in the LoadXMLData()
fragments, you will have to use TXMLDocument directly instead since LoadXMLData()
does not let you set the poPreserveWhiteSpace
flag:
FragmentXMLObj := TXMLDocument.Create(self);
FragmentXMLObj.ParseOptions := FragmentXMLObj.ParseOptions + [poPreserveWhiteSpace];
FragmentXMLObj.Options := [doNodeAutoCreate, doNodeAutoIndent];
FragmentXMLObj.LoadFromXML(XMLFragment.Text);
FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(FragmentXMLObj.DocumentElement);
FragmentXMLObj.Free;
To avoid any problem with ChildNodes
indexes, you are better off using an XPath query instead, so you can let the DOM search for the <Class>
node that you want to insert fragments into.
Either way, you will soon discover that this does not produce very nice looking XML. If you just want there to be whitespace present, but you don't actually need to preserve the original whitespace as-is, then you are better off disabling the poPreserveWhiteSpace
flag, and then use FormatXMLData()
when you are saving the final document:
XMLMerged.Text := FormatXMLData(StoredXMLObj.XML.Text);