Operations on XML Elements |
|
Adding an Element to the Document |
Introduction |
Consider this: So far, to create an XML element, we were directly typing it in a file. When such a file had been created and saved, it was ready to be processed. Here is an example of a file named names.xml: |
<?xml version="1.0" encoding="UTF-8"?> <names> <name>Francine Mukoko</name> <name>Gertrude Monay</name> <name>Paul Bertrand Yamaguchi</name> </names> //--------------------------------------------------------------------------- void __fastcall TfrmMain::FormCreate(TObject *Sender) { // Locate the root node IXMLNode *nodRoot = docNames->DocumentElement; IXMLNodeList *lstNames = nodRoot->ChildNodes; for(int i = 0; i < lstNames->Count; i++) lbxNames->Items->Add(lstNames[0].Get(i)->Text); } //--------------------------------------------------------------------------- In some applications, you will want the user to provide you with the necessary value(s) to create an element. Fortunately, the TXMLDocument class, the TXMLNodeList class, and the IXMLNode interface, and the IXMLNodeList interface provide all the necessary properties and methods to perform the routine operations of an XML file, an element, or a node. The operations include locating a node, adding a new element, deleting a node, or removing all nodes.
To assist with programmatically creating a new element, the IXMLNode interface is equipped with a method named CreateElement. Its syntax is: IXMLNode CreateElement(const DOMString TagOrData, const DOMString NamespaceURI); The first argument to this method is the name of the element that you will add. The second argument is the namespace of the node's definition. For our simple example, you can pass this argument as an empty string. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateElementClick(TObject *Sender) { IXMLNode *xmlRoot = docNames->DocumentElement; _di_IXMLNode nodElement = docNames->CreateElement(L"name", L""); } //--------------------------------------------------------------------------- After calling the TXMLDocument::CreateElement() method, you can pass its returned value to the Add() method of the ChildNodes property of the the node that will be the parent of the new node. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateElementClick(TObject *Sender) { IXMLNode *xmlRoot = docNames->DocumentElement; _di_IXMLNode nodElement = docNames->CreateElement(L"name", L""); xmlRoot->ChildNodes->Add(nodElement); } //--------------------------------------------------------------------------- At this time, the new node exists only in the computer memory. If you want to add it to the file, you must save it, which you can do by calling the TXMLDocument::SaveToFile() method. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateNodeClick(TObject *Sender) { IXMLNode *xmlRoot = docNames->DocumentElement; _di_IXMLNode nodElement = docNames->CreateElement(L"name", L""); xmlRoot->ChildNodes->Add(nodElement); docNames->SaveToFile(docNames->FileName); ShowMessage(L"The new node has been added"); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <names> <name>Francine Mukoko</name> <name>Gertrude Monay</name> <name>Paul Bertrand Yamaguchi</name> <name/> </names> If you do not want the node to be empty, you can specify its value by assigning it to its Text property. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateElementClick(TObject *Sender) { IXMLNode *xmlRoot = docNames->DocumentElement; _di_IXMLNode nodElement = docNames->CreateElement(L"name", L""); nodElement->Text = L"Patricia Katts"; xmlRoot->ChildNodes->Add(nodElement); docNames->SaveToFile(docNames->FileName); ShowMessage(L"The new element has been added"); // Locate the root node IXMLNode *nodRoot = docNames->DocumentElement; IXMLNodeList *lstNames = nodRoot->ChildNodes; lbxNames->Items->Clear(); for(int i = 0; i < lstNames->Count; i++) lbxNames->Items->Add(lstNames[0].Get(i)->Text); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <names> <name>Francine Mukoko</name> <name>Gertrude Monay</name> <name>Paul Bertrand Yamaguchi</name> <name>Patricia Katts</name> </names>
In the previous implementations, we saw how to create a node that is a child of the document element. Consider the following document: <?xml version="1.0" encoding="UTF-8"?> <videos> <video> <title>The Distinguished Gentleman</title> <director>Jonathan Lynn</director> <length>112 Minutes</length> <format>DVD</format> <rating>R</rating> </video> <video> <title>Her Alibi</title> <director>Bruce Beresford</director> <length>94 Minutes</length> <format>DVD</format> <rating>PG-13</rating> </video> </videos> In a typical XML document like this one, you may want to create a node that is a child of another element. For example, from the above document, you may want to create a video node that contains its own child nodes. In order to create a child node of a node, of course you must first determine the node that will act as parent. You have two main options. You may want to first create the node that will be the parent. You can create that node as a child of the document element. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateVideoClick(TObject *Sender) { // Adding a new empty element IXMLNode *xmlRoot = docVideos->DocumentElement; _di_IXMLNode nodElement = docVideos->CreateElement(L"video", L""); xmlRoot->ChildNodes->Add(nodElement); docVideos->SaveToFile(docVideos->FileName); ShowMessage(L"The new video has been added"); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video> <title>The Distinguished Gentleman</title> <director>Jonathan Lynn</director> <length>112 Minutes</length> <format>DVD</format> <rating>R</rating> </video> <video> <title>Her Alibi</title> <director>Bruce Beresford</director> <length>94 Minutes</length> <format>DVD</format> <rating>PG-13</rating> </video> <video/> </videos> The second option is that you can use and identify an existing node that will be the parent of the new node. Even in this case you have many options:
After specifying or identifying the node that will be the parent, you can add the new node to it. to support this operation, the IXMLNode interface is equipped with a method named AddChild that is overloaded in two versions. Their syntaxes are: IXMLNode AddChild(const DOMString TagName, int Index = -1); IXMLNode AddChild(const DOMString TagName, const DOMString NamespaceURI, bool GenPrefix = False, int Index = -1); The first argument is the name of the tag you want to create. Index is the position, among the existing (if any) node of the parent, where the new node will be added. Because this argument is optional, if either you do not pass it or you pass it as -1, the new node will added as the last child. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateVideoClick(TObject *Sender) { // Get a reference to the document element IXMLNode *xmlRoot = docVideos->DocumentElement; // Add a new empty element _di_IXMLNode nodElement = docVideos->CreateElement(L"video", L""); xmlRoot->ChildNodes->Add(nodElement); // Create a new node named "title" as a child of the above video node IXMLNode *nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"title"); // Optionally specify the value of the new node nodVideo->Text = L"Not Another Teen Movie"; // Save the file (optional, but why not?) docVideos->SaveToFile(docVideos->FileName); // Let the user know that the new node has been created ShowMessage(L"The new video has been added"); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video> <title>The Distinguished Gentleman</title> <director>Jonathan Lynn</director> <length>112 Minutes</length> <format>DVD</format> <rating>R</rating> </video> <video> <title>Her Alibi</title> <director>Bruce Beresford</director> <length>94 Minutes</length> <format>DVD</format> <rating>PG-13</rating> </video> <video> <title>Not Another Teen Movie</title> </video> </videos> In the same way, you can create other child nodes of the same parent. Here are examples: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateVideoClick(TObject *Sender) { // Get a reference to the document element IXMLNode *xmlRoot = docVideos->DocumentElement; // Add a new empty element _di_IXMLNode nodElement = docVideos->CreateElement(L"video", L""); xmlRoot->ChildNodes->Add(nodElement); // Create a new node named "title" as a child of the above video node IXMLNode *nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"title"); // Optionally specify the value of the new node nodVideo->Text = L"Not Another Teen Movie"; // Create other child nodes nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"director"); nodVideo->Text = L"Joel Gallen"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"length"); nodVideo->Text = L"100 Minutes"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"format"); nodVideo->Text = L"DVD"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"rating"); nodVideo->Text = L"Unrated"; // Save the file (optional, but why not?) docVideos->SaveToFile(docVideos->FileName); // Let the user know that the new node has been created ShowMessage(L"The new video has been added"); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video> <title>The Distinguished Gentleman</title> <director>Jonathan Lynn</director> <length>112 Minutes</length> <format>DVD</format> <rating>R</rating> </video> <video> <title>Her Alibi</title> <director>Bruce Beresford</director> <length>94 Minutes</length> <format>DVD</format> <rating>PG-13</rating> </video> <video> <title>Not Another Teen Movie</title> <director>Joel Gallen</director> <length>100 Minutes</length> <format>DVD</format> <rating>Unrated</rating> </video> </videos>
Once again, consider the following document: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> Imagine you want to add a new node between two existing nodes. To support this operation, the IXMLNodeList interface is equipped with a method named Insert. Its syntax is: Insert(int Index, const IXMLNode Node); This method takes two arguments. the second argument represents the node object you want to create. The first argument represents the index that the new node will occupy. The index is zero-based. This means that, to add the new node as the first, pass this argument as 0. To add the new node as the second, pass the index as 1, and so on. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnNodeClick(TObject *Sender) { IXMLNode *xmlRoot = docVideos->DocumentElement; _di_IXMLNode nodElement = docVideos->CreateElement(L"video", L""); nodElement->Text = L"The Day After Tomorrow"; xmlRoot->ChildNodes->Insert(1, nodElement); docVideos->SaveToFile(docVideos->FileName); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>The Day After Tomorrow</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> In the same way, you can first locate a node, and then insert a new node before or after it.
Just as you can create a child node, you can use the same approach or similar steps to add a grand child of a node. To start, first locate or specify the parent node that will actually be the grand-parent. Then locate the child that will be the parent. Then create the new node as a child of this parent node.: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnCreateVideoClick(TObject *Sender) { // Get a reference to the document element IXMLNode *xmlRoot = docVideos->DocumentElement; // Add a new empty element _di_IXMLNode nodElement = docVideos->CreateElement(L"video", L""); xmlRoot->ChildNodes->Add(nodElement); // Create a new node named "title" as a child of the above video node IXMLNode *nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"title"); // Optionally specify the value of the new node nodVideo->Text = L"Not Another Teen Movie"; // Create other child nodes nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"director"); nodVideo->Text = L"Joel Gallen"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"length"); nodVideo->Text = L"100"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"format"); nodVideo->Text = L"DVD"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"rating"); nodVideo->Text = L"Unrated"; nodVideo = xmlRoot->ChildNodes->Last()->AddChild(L"actors"); IXMLNode *nodActor = nodVideo->AddChild(L"actor"); nodActor->Text = L"Jaime Pressly"; nodActor = nodVideo->AddChild(L"actor"); nodActor->Text = L"Mia Kirshner"; nodActor = nodVideo->AddChild(L"actor"); nodActor->Text = L"Randy Quaid"; // Save the file (optional, but why not?) docVideos->SaveToFile(docVideos->FileName); // Let the user know that the new node has been created ShowMessage(L"The new video has been added"); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video> <title>The Distinguished Gentleman</title> <director>Jonathan Lynn</director> <length>112 Minutes</length> <format>DVD</format> <rating>R</rating> </video> <video> <title>Her Alibi</title> <director>Bruce Beresford</director> <length>94 Minutes</length> <format>DVD</format> <rating>PG-13</rating> </video> <video> <title>Not Another Teen Movie</title> <director>Joel Gallen</director> <length>100 Minutes</length> <format>DVD</format> <rating>Unrated</rating> <actors> <actor>Jaime Pressly</actor> <actor>Mia Kirshner</actor> <actor>Randy Quaid</actor> </actors> </video> </videos> Using the same approach, you can add children to children of children, and so on.
In some cases, you may want to perform an operation on an existing and particular node. For example, you may want to change the value of a node, you may want to add a new child node to an existing node, you may want to delete a node, etc. Before taking any of these actions, you must be able to locate or identify the desired element. Locating an element consists of looking for a particular node among the nodes. To do this, you must start somewhere. Obviously, the first node you can identify is the root. Once you get the root, you can then get a collection of its children. After getting a collection of the children of the root, you can locate a node in the collection. If the node you are looking for is a child of that first collection, you can then get a collection of the child nodes of that node and proceed.
Consider the following XML file named videos.xml: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> In the previous lesson, we saw that the IXMLNodeList class was equipped with the overloaded [] operator that allows you to access a node by its index. Here are examples: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnLocateClick(TObject *Sender) { // Locate the root node IXMLNode *nodRoot = docVideos->DocumentElement; IXMLNodeList *lstVideos = nodRoot->ChildNodes; for(int i = 0; i < lstVideos->Count; i++) ShowMessage(lstVideos[0][i]->Text); } //--------------------------------------------------------------------------- This would produce:
To provide you with an alternative, the IXMLNodeList interface is equipped with a property named Nodes: __property IXMLNode Nodes[OleVariant const IndexOrName]; This property uses the [] operator to produce an IXMLNode object: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnNodeClick(TObject *Sender) { IXMLNode *nodRoot = docVideos->DocumentElement; IXMLNode *nodTitle = nodRoot->ChildNodes->Nodes[0]; ShowMessage(nodTitle->Text); } //--------------------------------------------------------------------------- You can use this characteristic to locate a node. Because XML is very flexible with the names (you can have two child nodes that have the same name) and values (you can have two child nodes that have the same value) of nodes, when creating an XML file, it is your responsibility to create a scheme that would eventually allow you to uniquely identify each element. Once you have located a node, you can then perform whatever operation you want on it. For example, you may want to create one or more children on an existing node. To do this, you can call the AddChild() method of that node. Consider this document: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> Here is an example that locates a node whose value is Not Another Teen Movie and add some child nodes to it //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnLocateClick(TObject *Sender) { // Locate the root node IXMLNode *nodRoot = docVideos->DocumentElement; IXMLNodeList *lstVideos = nodRoot->ChildNodes; // Check each child node of the root for(int i = 0; i < lstVideos->Count; i++) { // Look for a node whose value is "Not Another Teen Movie // If you find it... if( lstVideos[0][i]->Text == L"Not Another Teen Movie" ) { // ... get a reference to that node _di_IXMLNode nodElement = lstVideos[0][i]; // Create a child node to it and name it director IXMLNode *nodVideo = nodElement->AddChild(L"director"); // Specify the value of the new child node nodVideo->Text = L"Joel Gallen"; // In the same way, add other child nodes nodVideo = nodElement->AddChild(L"length"); nodVideo->Text = L"100 Minutes"; nodVideo = nodElement->AddChild(L"format"); nodVideo->Text = L"DVD"; nodVideo = nodElement->AddChild(L"rating"); nodVideo->Text = L"Unrated"; // Save the file (if you want) docVideos->SaveToFile(docVideos->FileName); // Let the user know that the new node has been created ShowMessage(L"The new video has been added"); } } } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>Not Another Teen Movie <director>Joel Gallen</director> <length>100 Minutes</length> <format>DVD</format> <rating>Unrated</rating> </video> <video>Her Alibi</video> </videos> In the same way, you should be able to locate any node by applying the indices to its children collection, and perform the desired action.
When examining the contents of an XML document, you may want to find one particular node. To assist you with this operating, the IXMLNodeList interface is equipped with a method named FindNode. The IXMLNodeList::FindNode() method is overloaded in three versions whose syntaxes are: IXMLNode FindNode(DOMString NodeName); IXMLNode FindNode(DOMString NodeName, DOMString NamespaceURI); IXMLNode FindNode(TGuid ChildNodeType); We are more interested in the first version. The NodeName argument is the name of the node you would like to find. When calling this method, you pass it a string. The parser starts checking the document from top to bottom. The first time it finds a node named as the argument, it returns its reference. If the parser does not find a node that uses the name specified as argument, the method returns NULL. Consider the following document: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> Here is an example of calling the IXMLNodeList::FindNode() method: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnNodeClick(TObject *Sender) { IXMLNode *nodRoot = docVideos->DocumentElement; IXMLNode *nodElement = nodRoot->ChildNodes->FindNode(L"video"); if( nodElement != NULL ) ShowMessage(nodElement->Text); } //--------------------------------------------------------------------------- This would produce: As stated already, if you pass a string but there is no node that uses that string as its name, the method returns NULL. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnNodeClick(TObject *Sender) { IXMLNode *nodRoot = docVideos->DocumentElement; IXMLNode *nodElement = nodRoot->ChildNodes->FindNode(L"stuff"); if( nodElement == NULL ) ShowMessage(L"There is no node with that name"); } //--------------------------------------------------------------------------- When calling the IXMLNodeList::FindNode() method, you have to specify the level of nodes where the parser should operate. For example if you have the following file: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> A call to the IXMLNodeList::FindNode(L"video") method as: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnNodeClick(TObject *Sender) { IXMLNode *nodRoot = docVideos->DocumentElement; IXMLNode *nodElement = nodRoot->ChildNodes->FindNode(L"video"); if( nodElement != NULL ) { if( nodElement->IsTextElement == true ) ShowMessage(nodElement->Text); else ShowMessage(L"The node ain't got no value"); } } //--------------------------------------------------------------------------- from the following document: <?xml version="1.0" encoding="utf-8"?> <videos> <video> <title>The Distinguished Gentleman</title> <director>Jonathan Lynn</director> <rating>PG-13</rating></video> <video>The Day After Tomorrow <director>Jonathan Lynn</director> <length>123 Minutes</length> </video> <video>The General's Daughter <rating>PG-13</rating> <yearreleased>1999</yearreleased> </video> </videos> would produce an empty string.
As long as you can locate a node, you can get to one of its siblings, if any. To support this operation, the IXMLNodeList interface is equipped with a method named FindSibling. Its syntax is: IXMLNode FindSibling(const IXMLNode Node, int Delta); The first argument of this method is a pointer to the node object to look for. The second argument specifies the relative index of the node with regards to the first argument.
If you have a node you do not want or do not need anymore in an XML document, you can delete it. To support this operation, the IXMLNodeList interface is equipped with a method named Remove. Its syntax is: int Remove(const IXMLNode Node); This method takes as argument the node to delete. If the node exists, it would be deleted and the method would return the index it was occupying. Obviously, in order to delete a node, you must find a way to define it so the parser can find it. The IXMLNodeList::Remove() method requires that you first define a node before deleting it. An alternative is to delete a node based on its name. To support this approach, the IXMLNodeList is equipped with a method named Delete. This method is overloaded with three versions. If you want to delete a node by specifying its name, you can use the following syntax of the method: int Delete(const DOMString Name); This method expects the name of the node as argument. When the method is called, the parser looks for a node that uses that name. If it finds it, it deletes it and returns the index it had. Here is an example: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Distinguished Gentleman</video> <video>The Day After Tomorrow</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnDeleteClick(TObject *Sender) { IXMLNode *xmlRoot = docVideos->DocumentElement; int Index = xmlRoot->ChildNodes->Delete(L"video"); docVideos->SaveToFile(docVideos->FileName); ShowMessage("The node positioned at " + AnsiString(Index) + " has been deleted"); } //--------------------------------------------------------------------------- This would produce: <?xml version="1.0" encoding="UTF-8"?> <videos> <video>The Day After Tomorrow</video> <video>Not Another Teen Movie</video> <video>Her Alibi</video> </videos> As you can see, when this (version of the) method is called, the parser looks for the node with the name passed as argument. If/when it finds the first node with that name, it deletes it, returns its index, and stops. If there is no node with that name, the method returns -1 (it does not throw an exception). Notice that the IXMLNodeList::Remove(const DOMString Name) method deletes the first instance of the node with the specified name. If there are many nodes with that same name, only the first would be deleted and the others would be kept. This approach can be vague at times. As an alternative, if you know the index of the node you want to delete, you can call the following version of the method: int Delete(const int Index); This version of the method takes a constant integer as the index of the node to be deleted. The index is zero-based. If the node is found, it is deleted and the method returns the index that node had. Here is an example: //--------------------------------------------------------------------------- void __fastcall TfrmMain::btnDeleteClick(TObject *Sender) { IXMLNode *xmlRoot = docVideos->DocumentElement; int Index = xmlRoot->ChildNodes->Delete(2); docVideos->SaveToFile(docVideos->FileName); ShowMessage("The node positioned at " + AnsiString(Index) + " has been deleted"); } //--------------------------------------------------------------------------- The third alternative is to specify the name of the node and its namespace. This alternative uses the following syntax of the IXMLNodeList::Remove() method: int Delete(const DOMString Name, const DOMString NamespaceURI);
To delete all nodes of a node, you can call the Clear() method of the IXMLNodeList interface. Its syntax is: void Clear(); When this method is called, it deletes all nodes, if any, of an XML document. |
|
||
Previous | Copyright © 2008-2009 FunctionX, Inc. | Home |
|