Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions docs/manual.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1377,7 +1377,7 @@ include::samples/modify_base.cpp[tags=attr]
[[modify.add]]
=== Adding nodes/attributes

[[xml_node::prepend_attribute]][[xml_node::append_attribute]][[xml_node::insert_attribute_after]][[xml_node::insert_attribute_before]][[xml_node::prepend_child]][[xml_node::append_child]][[xml_node::insert_child_after]][[xml_node::insert_child_before]]
[[xml_node::prepend_attribute]][[xml_node::append_attribute]][[xml_node::insert_attribute_after]][[xml_node::insert_attribute_before]][[xml_node::ensure_attribute]][[xml_node::prepend_child]][[xml_node::append_child]][[xml_node::insert_child_after]][[xml_node::insert_child_before]][[xml_node::ensure_child]]
Nodes and attributes do not exist without a 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:

[source]
Expand All @@ -1404,9 +1404,14 @@ xml_node xml_node::insert_child_after(const char_t* name, const xml_node& node);
xml_node xml_node::insert_child_after(string_view_t name, const xml_node& node);
xml_node xml_node::insert_child_before(const char_t* name, const xml_node& node);
xml_node xml_node::insert_child_before(string_view_t name, const xml_node& node);

xml_attribute xml_node::ensure_attribute(const char_t* name);
xml_attribute xml_node::ensure_attribute(string_view_t name);
xml_node xml_node::ensure_child(const char_t* name);
xml_node xml_node::ensure_child(string_view_t name);
----

`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; `prepend_attribute` and `prepend_child` create a new node/attribute at the beginning of the list; `insert_attribute_after`, `insert_attribute_before`, `insert_child_after` and `insert_child_before` add the node/attribute before or after the specified node/attribute.
`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; `prepend_attribute` and `prepend_child` create a new node/attribute at the beginning of the list; `insert_attribute_after`, `insert_attribute_before`, `insert_child_after` and `insert_child_before` add the node/attribute before or after the specified node/attribute. `ensure_attribute` and `ensure_child` return the existing attribute/child with the specified name, appending a new one only if no such attribute/child exists; this makes it convenient to write code like `node.ensure_attribute("id") = 123;`.

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 with the `type` argument 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. Node functions with the `name` argument create the element node (<<node_element,node_element>>) with the specified name.

Expand All @@ -1422,7 +1427,7 @@ All functions return the handle to the created object on success, and null handl

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 `node.attribute("id") = 123;` will not do anything if `node` does not have an attribute with name `"id"`. Make sure you're operating with existing attributes/nodes by adding them if necessary.
CAUTION: `attribute()` and `child()` functions do not add attributes or nodes to the tree, so code like `node.attribute("id") = 123;` will not do anything if `node` does not have an attribute with name `"id"`. Make sure you're operating with existing attributes/nodes by adding them if necessary, or use `ensure_attribute`/`ensure_child`.

This is an example of adding new attributes/nodes to the document (link:samples/modify_add.cpp[]):

Expand Down Expand Up @@ -2208,6 +2213,7 @@ Anniversary release (pugixml turns 20 this year!). Changes:

* Improvements:
. `PUGIXML_CHARCONV_FLOAT` option can be enabled to switch floating point conversions to `<charconv>`; this requires C{plus}{plus}17, makes the conversions locale-independent and can improve performance
. Add `xml_node::ensure_child` and `xml_node::ensure_attribute` that return the child/attribute with the specified name, adding one if it does not exist
. Improve performance of searching for nodes and attributes by name
. Loading a document from an empty buffer no longer performs memory allocations

Expand Down Expand Up @@ -3079,6 +3085,11 @@ const unsigned int +++<a href="#parse_wnorm_attribute">parse_wnorm_attribute</a>
xml_node +++<a href="#xml_node::insert_child_before">insert_child_before</a>+++(const char_t* name, const xml_node& node);
xml_node +++<a href="#xml_node::insert_child_before">insert_child_before</a>+++(string_view_t name, const xml_node& node);

xml_attribute +++<a href="#xml_node::ensure_attribute">ensure_attribute</a>+++(const char_t* name);
xml_attribute +++<a href="#xml_node::ensure_attribute">ensure_attribute</a>+++(string_view_t name);
xml_node +++<a href="#xml_node::ensure_child">ensure_child</a>+++(const char_t* name);
xml_node +++<a href="#xml_node::ensure_child">ensure_child</a>+++(string_view_t name);

xml_attribute +++<a href="#xml_node::append_copy">append_copy</a>+++(const xml_attribute& proto);
xml_attribute +++<a href="#xml_node::prepend_copy">prepend_copy</a>+++(const xml_attribute& proto);
xml_attribute +++<a href="#xml_node::insert_copy_after">insert_copy_after</a>+++(const xml_attribute& proto, const xml_attribute& attr);
Expand Down
23 changes: 18 additions & 5 deletions docs/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -2807,7 +2807,7 @@ <h3 id="modify.attrdata"><a class="anchor" href="#modify.attrdata"></a><a class=
<div class="sect2">
<h3 id="modify.add"><a class="anchor" href="#modify.add"></a><a class="link" href="#modify.add">6.3. Adding nodes/attributes</a></h3>
<div class="paragraph">
<p><a id="xml_node::prepend_attribute"></a><a id="xml_node::append_attribute"></a><a id="xml_node::insert_attribute_after"></a><a id="xml_node::insert_attribute_before"></a><a id="xml_node::prepend_child"></a><a id="xml_node::append_child"></a><a id="xml_node::insert_child_after"></a><a id="xml_node::insert_child_before"></a>
<p><a id="xml_node::prepend_attribute"></a><a id="xml_node::append_attribute"></a><a id="xml_node::insert_attribute_after"></a><a id="xml_node::insert_attribute_before"></a><a id="xml_node::ensure_attribute"></a><a id="xml_node::prepend_child"></a><a id="xml_node::append_child"></a><a id="xml_node::insert_child_after"></a><a id="xml_node::insert_child_before"></a><a id="xml_node::ensure_child"></a>
Nodes and attributes do not exist without a document tree, so you can&#8217;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:</p>
</div>
<div class="listingblock">
Expand All @@ -2833,11 +2833,16 @@ <h3 id="modify.add"><a class="anchor" href="#modify.add"></a><a class="link" hre
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::insert_child_after</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span>
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::insert_child_after</span><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span>
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::insert_child_before</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span>
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::insert_child_before</span><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span></code></pre>
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::insert_child_before</span><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span>

<span class="tok-n">xml_attribute</span><span class="tok-w"> </span><span class="tok-nf">xml_node::ensure_attribute</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>
<span class="tok-n">xml_attribute</span><span class="tok-w"> </span><span class="tok-nf">xml_node::ensure_attribute</span><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::ensure_child</span><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>
<span class="tok-n">xml_node</span><span class="tok-w"> </span><span class="tok-nf">xml_node::ensure_child</span><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><code>append_attribute</code> and <code>append_child</code> create a new node/attribute at the end of the corresponding list of the node the method is called on; <code>prepend_attribute</code> and <code>prepend_child</code> create a new node/attribute at the beginning of the list; <code>insert_attribute_after</code>, <code>insert_attribute_before</code>, <code>insert_child_after</code> and <code>insert_child_before</code> add the node/attribute before or after the specified node/attribute.</p>
<p><code>append_attribute</code> and <code>append_child</code> create a new node/attribute at the end of the corresponding list of the node the method is called on; <code>prepend_attribute</code> and <code>prepend_child</code> create a new node/attribute at the beginning of the list; <code>insert_attribute_after</code>, <code>insert_attribute_before</code>, <code>insert_child_after</code> and <code>insert_child_before</code> add the node/attribute before or after the specified node/attribute. <code>ensure_attribute</code> and <code>ensure_child</code> return the existing attribute/child with the specified name, appending a new one only if no such attribute/child exists; this makes it convenient to write code like <code>node.ensure_attribute("id") = 123;</code>.</p>
</div>
<div class="paragraph">
<p>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 with the <code>type</code> argument create the node with the specified type; since node type can&#8217;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. Node functions with the <code>name</code> argument create the element node (<a href="#node_element">node_element</a>) with the specified name.</p>
Expand Down Expand Up @@ -2880,7 +2885,7 @@ <h3 id="modify.add"><a class="anchor" href="#modify.add"></a><a class="link" hre
<div class="title">Caution</div>
</td>
<td class="content">
<code>attribute()</code> and <code>child()</code> functions do not add attributes or nodes to the tree, so code like <code>node.attribute("id") = 123;</code> will not do anything if <code>node</code> does not have an attribute with name <code>"id"</code>. Make sure you&#8217;re operating with existing attributes/nodes by adding them if necessary.
<code>attribute()</code> and <code>child()</code> functions do not add attributes or nodes to the tree, so code like <code>node.attribute("id") = 123;</code> will not do anything if <code>node</code> does not have an attribute with name <code>"id"</code>. Make sure you&#8217;re operating with existing attributes/nodes by adding them if necessary, or use <code>ensure_attribute</code>/<code>ensure_child</code>.
</td>
</tr>
</table>
Expand Down Expand Up @@ -4153,6 +4158,9 @@ <h3 id="v1.16"><a class="anchor" href="#v1.16"></a><a class="link" href="#v1.16"
<p><code>PUGIXML_CHARCONV_FLOAT</code> option can be enabled to switch floating point conversions to <code>&lt;charconv&gt;</code>; this requires C&#43;&#43;17, makes the conversions locale-independent and can improve performance</p>
</li>
<li>
<p>Add <code>xml_node::ensure_child</code> and <code>xml_node::ensure_attribute</code> that return the child/attribute with the specified name, adding one if it does not exist</p>
</li>
<li>
<p>Improve performance of searching for nodes and attributes by name</p>
</li>
<li>
Expand Down Expand Up @@ -6128,6 +6136,11 @@ <h3 id="apiref.classes"><a class="anchor" href="#apiref.classes"></a><a class="l
<span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-w"> </span><a href="#xml_node::insert_child_before">insert_child_before</a><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span>
<span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-w"> </span><a href="#xml_node::insert_child_before">insert_child_before</a><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">node</span><span class="tok-p">);</span>

<span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-w"> </span><a href="#xml_node::ensure_attribute">ensure_attribute</a><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>
<span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-w"> </span><a href="#xml_node::ensure_attribute">ensure_attribute</a><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>
<span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-w"> </span><a href="#xml_node::ensure_child">ensure_child</a><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">char_t</span><span class="tok-o">*</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>
<span class="tok-w"> </span><span class="tok-n">xml_node</span><span class="tok-w"> </span><a href="#xml_node::ensure_child">ensure_child</a><span class="tok-p">(</span><span class="tok-n">string_view_t</span><span class="tok-w"> </span><span class="tok-n">name</span><span class="tok-p">);</span>

<span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-w"> </span><a href="#xml_node::append_copy">append_copy</a><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">proto</span><span class="tok-p">);</span>
<span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-w"> </span><a href="#xml_node::prepend_copy">prepend_copy</a><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">proto</span><span class="tok-p">);</span>
<span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-w"> </span><a href="#xml_node::insert_copy_after">insert_copy_after</a><span class="tok-p">(</span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">proto</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">const</span><span class="tok-w"> </span><span class="tok-n">xml_attribute</span><span class="tok-o">&amp;</span><span class="tok-w"> </span><span class="tok-n">attr</span><span class="tok-p">);</span>
Expand Down Expand Up @@ -6375,7 +6388,7 @@ <h3 id="apiref.functions"><a class="anchor" href="#apiref.functions"></a><a clas
</div>
<div id="footer">
<div id="footer-text">
Last updated 2026-06-13 16:47:23 -0700
Last updated 2026-06-14 22:10:08 -0700
</div>
</div>
</body>
Expand Down
32 changes: 32 additions & 0 deletions src/pugixml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6256,6 +6256,22 @@ namespace pugi
}
#endif

PUGI_IMPL_FN xml_attribute xml_node::ensure_attribute(const char_t* name_)
{
xml_attribute result = attribute(name_);

return result ? result : append_attribute(name_);
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_attribute xml_node::ensure_attribute(string_view_t name_)
{
xml_attribute result = attribute(name_);

return result ? result : append_attribute(name_);
}
#endif

PUGI_IMPL_FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
{
if (!proto) return xml_attribute();
Expand Down Expand Up @@ -6470,6 +6486,22 @@ namespace pugi
}
#endif

PUGI_IMPL_FN xml_node xml_node::ensure_child(const char_t* name_)
{
xml_node result = child(name_);

return result ? result : append_child(name_);
}

#ifdef PUGIXML_HAS_STRING_VIEW
PUGI_IMPL_FN xml_node xml_node::ensure_child(string_view_t name_)
{
xml_node result = child(name_);

return result ? result : append_child(name_);
}
#endif

PUGI_IMPL_FN xml_node xml_node::append_copy(const xml_node& proto)
{
xml_node_type type_ = proto.type();
Expand Down
Loading
Loading