Reading, Traversing, and Modifying XML
You have seen how to create and annotate XML content using
the LINQ to XML API. Whenever we have XML available in memory, we can also
navigate and eventually modify it. To navigate XML content, we can use methods
and properties of X* classes, or we can also rely on LINQ
queries over an X* object. In this section, we will see the former way of
working, and in the next section we will see the latter.
Every XNode provides some methods and
properties to navigate its hierarchy. For instance, we can use IsAfter
or IsBefore methods to compare ordinal positioning of
nodes in the document. These methods internally use the CompareDocumentOrder
static method of the XNode class and return a numeric
index, of type Integer, that represents the “distance"
of two XNode instances in the containing
XDocument. It is also used by XNodeDocumentOrderComparer,
and it is useful when ordering nodes in LINQ to XML queries. Every
XNode also provides a couple of properties, called NextNode
and PreviousNode, that map to the next and previous
nodes in the graph, as their names indicate. Pay attention to the relative cost
of these properties. NextNode returns a reference to an
internal field and is relatively cheap; PreviousNode requires
a partial scan of the tree branch containing the current node and is a little
bit more expensive. XContainer also provides
LastNode and FirstNode properties. Finally,
every XObject offers a Parent property
that represents the parent node in the graph.
Whenever you find a node traversing the document using one of these
techniques and you want to modify it, you can use methods such as
Remove or ReplaceWith, which are available for
any XNode, to remove the node itself from the graph or
to replace it with a new fragment. There are also RemoveAttributes,
ReplaceAttributes, and ReplaceAll
for objects of type XElement, which work with their
respective attributes or with the full set of child nodes. Finally,
XElement also offers SetAttributeValue,
SetElementValue, and SetValue to change the
value of an attribute, a child element, or the entire current element,
respectively.
In Listing 6-20, you can
see how to replace one tag with another.
Listing 6-20: Example
of tag replacement using the XElement ReplaceWith method
XElement customer = new XElement("customer",
new XAttribute("id", "C01"),
new XElement("firstName", "Paolo"),
new XElement("lastName", "Pialorsi"));
// Do something in the meantime ...
customer.LastNode.ReplaceWith(
new XElement("nickName", "PaoloPia"));
The preceding code block changes this XML:
into this XML:
Listing 6-21 shows you how to change
attribute and element values.
Listing 6-21: Example
of attribute and child element management using XElement methods
customer.SetAttributeValue("id", "C02");
customer.SetElementValue("notes", "Notes about this customer");
By calling these methods, the API creates attributes or elements
that do not yet exist or changes the values of ones that already exist. When
the value provided to these methods is NULL and the nodes already exist, they
are removed.
While traversing the XML, keep in mind that the navigation
technique you use influences the result. The methods and properties shown until
now work directly in memory and determine their results at the time that you
invoke them. If you ask to remove or replace a node, the action is taken
instantly within the in-memory structure. In the case of queries over XML,
based on the LINQ to XML query engine, modification methods are applied to
query expression results that will be evaluated only when they are effectively
used, like the ones you saw in
Chapter 4.