BUG? : XContainer.ReplaceContent throws an exception if attributes of the element are passed...
and they already exist on the element.
I am not saying it's a bug for sure, but if an element has an attribute, and you call ReplaceContent() on the element and specify the same attribute, you will get an exception. I tend to think it is a bug for the following reasons.
1. It has the same feel as the class constructors that allow multiple args to be passed. And, since when constructing an XElement you can specify attributes, it feels a little inconsistent that you cannot specify them when you call ReplaceContent. You could argue that attributes of an element are not considered content, but I think you could also say they are.
2. If you do not already have an attribute with the passed name, it will be added and handled properly. It is only if it already exists that an exception is thrown.
Here is some sample code:
XElement auto =
new XElement("Automobile",
new XAttribute("axles", 2),
new XElement("Make", "Ford"),
new XElement("Model", "Mustang"),
new XElement("Year", "2004"));
Console.WriteLine(auto);
auto.ReplaceContent(
new XAttribute("axles", 2),
new XElement("Make", "Chevrolet"),
new XElement("Model", "Impala"),
new XElement("Year", "2006"));
Console.WriteLine(auto);
When ReplaceContent is called, the following exception is thrown:
Unhandled Exception: System.InvalidOperationException: Duplicate attribute.
at System.Xml.XLinq.XElement.AddAttribute(XAttribute a)
at System.Xml.XLinq.XContainer.Add(Object content)
at System.Xml.XLinq.XContainer.Add(Object content)
at System.Xml.XLinq.XContainer.ReplaceContent(Object content)
at System.Xml.XLinq.XContainer.ReplaceContent(Object[] content)
at LINQChapter4.Program.Test() in c:\Documents and Settings\HP_Administrator\My Documents\Visual Studio 2005\Projects\LINQChapter
4\LINQChapter4\Program.cs:line 92
at LINQChapter4.Program.Main(String[] args) in c:\Documents and Settings\HP_Administrator\My Documents\Visual Studio 2005\Project
s\LINQChapter4\LINQChapter4\Program.cs:line 17
However, if you comment out that XAttribute line where the Automobile element is constructed, it works just fine, and the attribute is added to the element.
Thanks.
[2583 byte] By [
JoeRattz] at [2007-12-24]
Hi Joe,
There seems to be a little misunderstanding around the term 'content'. In LINQ to XML, 'content' refers to the nodes of the container. Note that it does not include attributes. With this clarification in mind, ReplaceContent() (ie. ReplaceNodes()) is just a shortcut for RemoveContent() (ie. RemoveNodes()) followed by Add(). In your sample, RemoveNodes() does not remove the existing attributes and Add() appends an attribute whose name conflicts with an existing attribute -- hence the 'Duplicate attribute' exception.
In a future version of LINQ to XML, the API will use the term 'nodes' instead of 'content' as to avoid such misunderstandings. The ReplaceAll() method will replace both the nodes and attributes of an element, the way your sample is trying to use the existing ReplaceContent(). Thanks for your feedback,
Ion
It seems like it will still be a little inconsistent, and potentially error prone, even after the API changes in the future version. Based on what I think you are saying, in the next version, the following changes will take place:
ReplaceContent() becomes ReplaceNodes() where ReplaceNodes() calls RemoveNodes() followed by Add().
The issue is that when calling ReplaceNodes(), it calls 2 methods, one aimed specifically at nodes (RemoveNodes()), and another that is generic and allows all types of LINQ to XML classes (object). It seems to me, it would be better if there was an AddNodes() method and ReplaceNodes() called it instead of Add(). That way, if you have code even trying to replace an attribute when calling ReplaceContent() it won't compile, as opposed to blowing up in production when it happens to have the wrong data.
Hi Joe,
We are aware of the slight asymmetry introduced by the XElement.ReplaceNodes() which allows one to add nodes and attributes whereas only the nodes of the element are removed. On the other hand, as you already point out in the first post, we would like all methods that perform addition to the tree to preserve the same "feel" as the functional construction. Conceptually, there is only one entry point in the API that performs such additions, namely Add(). It knows how to handle XNode, XAttribute, IEnumerable, string, etc. If you do prefer to have tighter control over the input data, the LINQ operators can provide some relief: AddNodes(IEnumerable nodes) is similar to Add(nodes.Cast<XNode>()). Cheers,
Ion