pugixml 0.9 manual | Overview | Installation | Document: Object model · Loading · Accessing · Modifying · Saving | XPath | API Reference | Table of Contents |
The document in pugixml is fully mutable: you can completely change the document structure and modify the data of nodes/attributes. This section provides documentation for the relevant functions. All functions take care of memory management and structural integrity themselves, so they always result in structurally valid tree - however, it is possible to create an invalid XML tree (for example, by adding two attributes with the same name or by setting attribute/node name to empty/invalid string). Tree modification is optimized for performance and for memory consumption, so if you have enough memory you can create documents from scratch with pugixml and later save them to file/stream instead of relying on error-prone manual text writing and without too much overhead.
All member functions that change node/attribute data or structure are non-constant
and thus can not be called on constant handles. However, you can easily convert
constant handle to non-constant one by simple assignment: void
foo(const pugi::xml_node& n)
{ pugi::xml_node nc = n; }
, so const-correctness
here mainly provides additional documentation.
As discussed before, nodes can have name and value, both of which are strings.
Depending on node type, name or value may be absent. node_document
nodes do not have name or value, node_element
and node_declaration
nodes
always have a name but never have a value, node_pcdata
,
node_cdata
and node_comment
nodes never have a name but
always have a value (it may be empty though), node_pi
nodes always have a name and a value (again, value may be empty). In order
to set node's name or value, you can use the following functions:
bool xml_node::set_name(const char_t* rhs); bool xml_node::set_value(const char_t* rhs);
Both functions try to set the name/value to the specified string, and return
the operation result. The operation fails if the node can not have name or
value (for instance, when trying to call set_name
on a node_pcdata
node), if
the node handle is null, or if there is insufficient memory to handle the
request. The provided string is copied into document managed memory and can
be destroyed after the function returns (for example, you can safely pass
stack-allocated buffers to these functions). The name/value content is not
verified, so take care to use only valid XML names, or the document may become
malformed.
There is no equivalent of child_value
function for modifying text children of the node.
This is an example of setting node name and value (samples/modify_base.cpp):
pugi::xml_node node = doc.child("node"); // change node name std::cout << node.set_name("notnode"); std::cout << ", new node name: " << node.name() << std::endl; // change comment text std::cout << doc.last_child().set_value("useless comment"); std::cout << ", new comment text: " << doc.last_child().value() << std::endl; // we can't change value of the element or name of the comment std::cout << node.set_value("1") << ", " << doc.last_child().set_name("2") << std::endl;
All attributes have name and value, both of which are strings (value may be empty). You can set them with the following functions:
bool xml_attribute::set_name(const char_t* rhs); bool xml_attribute::set_value(const char_t* rhs);
Both functions try to set the name/value to the specified string, and return the operation result. The operation fails if the attribute handle is null, or if there is insufficient memory to handle the request. The provided string is copied into document managed memory and can be destroyed after the function returns (for example, you can safely pass stack-allocated buffers to these functions). The name/value content is not verified, so take care to use only valid XML names, or the document may become malformed.
In addition to string functions, several functions are provided for handling attributes with numbers and booleans as values:
bool xml_attribute::set_value(int rhs); bool xml_attribute::set_value(unsigned int rhs); bool xml_attribute::set_value(double rhs); bool xml_attribute::set_value(bool rhs);
The above functions convert the argument to string and then call the base
set_value
function. Integers
are converted to a decimal form, floating-point numbers are converted to
either decimal or scientific form, depending on the number magnitude, boolean
values are converted to either "true"
or "false"
.
Caution | |
---|---|
Number conversion functions depend on current C locale as set with |
Note | |
---|---|
There are no portable 64-bit types in C++, so there is no corresponding
|
For convenience, all set_value
functions have the corresponding assignment operators:
xml_attribute& xml_attribute::operator=(const char_t* rhs); xml_attribute& xml_attribute::operator=(int rhs); xml_attribute& xml_attribute::operator=(unsigned int rhs); xml_attribute& xml_attribute::operator=(double rhs); xml_attribute& xml_attribute::operator=(bool rhs);
These operators simply call the right set_value
function and return the attribute they're called on; the return value of
set_value
is ignored, so
errors are not detected.
This is an example of setting attribute name and value (samples/modify_base.cpp):
pugi::xml_attribute attr = node.attribute("id"); // change attribute name/value std::cout << attr.set_name("key") << ", " << attr.set_value("345"); std::cout << ", new attribute: " << attr.name() << "=" << attr.value() << std::endl; // we can use numbers or booleans attr.set_value(1.234); std::cout << "new attribute value: " << attr.value() << std::endl; // we can also use assignment operators for more concise code attr = true; std::cout << "final attribute value: " << attr.value() << std::endl;
Nodes and attributes do not exist outside of document tree, so you can't create them without adding them to some document. A node or attribute can be created at the end of node/attribute list or before/after some other node:
xml_attribute xml_node::append_attribute(const char_t* name); xml_attribute xml_node::insert_attribute_after(const char_t* name, const xml_attribute& attr); xml_attribute xml_node::insert_attribute_before(const char_t* name, const xml_attribute& attr); xml_node xml_node::append_child(xml_node_type type = node_element); xml_node xml_node::insert_child_after(xml_node_type type, const xml_node& node); xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node);
append_attribute
and append_child
create a new node/attribute
at the end of the corresponding list of the node the method is called on;
insert_attribute_after
,
insert_attribute_before
,
insert_child_after
and insert_attribute_before
add the node/attribute
before or after specified node/attribute.
Attribute functions create an attribute with the specified name; you can specify the empty name and change the name later if you want to. Node functions create the node with the specified type; since node type can't be changed, you have to know the desired type beforehand. Also note that not all types can be added as children; see below for clarification.
All functions return the handle to newly created object on success, and null handle on failure. There are several reasons for failure:
node_element
nodes
can contain attributes, so attribute adding fails if node is not an element;
node_document
and
node_element
nodes can
contain children, so child node adding fails if target node is not an
element or a document;
node_document
and node_null
nodes can not be inserted
as children, so passing node_document
or node_null
value as
type results in operation failure;
node_declaration
nodes
can only be added as children of the document node; attempt to insert
declaration node as a child of an element node fails;
Even if the operation fails, the document remains in consistent state, but the requested node/attribute is not added.
Caution | |
---|---|
attribute() and child() functions do not add attributes or nodes to the
tree, so code like |
This is an example of adding new attributes/nodes to the document (samples/modify_add.cpp):
// add node with some name pugi::xml_node node = doc.append_child(); node.set_name("node"); // add description node with text child pugi::xml_node descr = node.append_child(); descr.set_name("description"); descr.append_child(pugi::node_pcdata).set_value("Simple node"); // add param node before the description pugi::xml_node param = node.insert_child_before(pugi::node_element, descr); param.set_name("param"); // add attributes to param node param.append_attribute("name") = "version"; param.append_attribute("value") = 1.1; param.insert_attribute_after("type", param.attribute("name")) = "float";
If you do not want your document to contain some node or attribute, you can remove it with one of the following functions:
bool xml_node::remove_attribute(const xml_attribute& a); bool xml_node::remove_child(const xml_node& n);
remove_attribute
removes
the attribute from the attribute list of the node, and returns the operation
result. remove_child
removes
the child node with the entire subtree (including all descendant nodes and
attributes) from the document, and returns the operation result. Removing
fails if one of the following is true:
Removing the attribute or node invalidates all handles to the same underlying object, and also invalidates all iterators pointing to the same object. Removing node also invalidates all past-the-end iterators to its attribute or child node list. Be careful to ensure that all such handles and iterators either do not exist or are not used after the attribute/node is removed.
If you want to remove the attribute or child node by its name, two additional helper functions are available:
bool xml_node::remove_attribute(const char_t* name); bool xml_node::remove_child(const char_t* name);
These functions look for the first attribute or child with the specified
name, and then remove it, returning the result. If there is no attribute
or child with such name, the function returns false
;
if there are two nodes with the given name, only the first node is deleted.
If you want to delete all nodes with the specified name, you can use code
like this: while (node.remove_child("tool")) ;
.
This is an example of removing attributes/nodes from the document (samples/modify_remove.cpp):
// remove description node with the whole subtree pugi::xml_node node = doc.child("node"); node.remove_child("description"); // remove id attribute pugi::xml_node param = node.child("param"); param.remove_attribute("value"); // we can also remove nodes/attributes by handles pugi::xml_attribute id = param.attribute("name"); param.remove_attribute(id);
With the help of previously described functions, it is possible to create trees with any contents and structure, including cloning the existing data. However since this is an often needed operation, pugixml provides built-in node/attribute cloning facilities. Since nodes and attributes do not exist outside of document tree, you can't create a standalone copy - you have to immediately insert it somewhere in the tree. For this, you can use one of the following functions:
xml_attribute xml_node::append_copy(const xml_attribute& proto); xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); xml_node xml_node::append_copy(const xml_node& proto); xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node); xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node);
These functions mirror the structure of append_child
,
insert_child_before
and related
functions - they take the handle to the prototype object, which is to be
cloned, insert a new attribute/node at the appropriate place, and then copy
the attribute data or the whole node subtree to the new object. The functions
return the handle to the resulting duplicate object, or null handle on failure.
The attribute is copied along with the name and value; the node is copied along with its type, name and value; additionally attribute list and all children are recursively cloned, resulting in the deep subtree clone. The prototype object can be a part of the same document, or a part of any other document.
The failure conditions resemble those of append_child
,
insert_child_before
and related
functions, consult their documentation
for more information. There are additional caveats specific to cloning
functions:
node_document
is not a valid insertion type. The example below provides a workaround.
node.append_copy(node.parent().parent());
.
This is a valid operation, and it results in a clone of the subtree in
the state before cloning started, i.e. no infinite recursion takes place.
This is an example with one possible implementation of include tags in XML (samples/include.cpp). It illustrates node cloning and usage of other document modification functions:
bool load_preprocess(pugi::xml_document& doc, const char* path); bool preprocess(pugi::xml_node node) { for (pugi::xml_node child = node.first_child(); child; ) { if (child.type() == pugi::node_pi && strcmp(child.name(), "include") == 0) { pugi::xml_node include = child; // load new preprocessed document (note: ideally this should handle relative paths) const char* path = include.value(); pugi::xml_document doc; if (!load_preprocess(doc, path)) return false; // insert the comment marker above include directive node.insert_child_before(pugi::node_comment, include).set_value(path); // copy the document above the include directive (this retains the original order!) for (pugi::xml_node ic = doc.first_child(); ic; ic = ic.next_sibling()) { node.insert_copy_before(ic, include); } // remove the include node and move to the next child child = child.next_sibling(); node.remove_child(include); } else { if (!preprocess(child)) return false; child = child.next_sibling(); } } return true; } bool load_preprocess(pugi::xml_document& doc, const char* path) { pugi::xml_parse_result result = doc.load_file(path, pugi::parse_default | pugi::parse_pi); // for <?include?> return result ? preprocess(doc) : false; }
pugixml 0.9 manual | Overview | Installation | Document: Object model · Loading · Accessing · Modifying · Saving | XPath | API Reference | Table of Contents |