I have a situation where I have an xml file that I don't want to modify.
The AddAnnotation function in XElement class provides an option to add memory-only data which is not serialized and not part of the XML.
I want to be able to save these annotations (for example: to another xml file) and then to deserialize both the xml and the annotations in order to get the same object I had.
I don't want to change the original xml and that's the reason that I use annotations.
To summarize, I want to be able to add custom data to an xml file. This data won't be a part of the xml when I serialize it or it will be a part of the xml but I would be able to retrieve the original xml easily.
Do you have any recommendation how I can do such a thing?
Edit: Should I use xml processing instructions? Are processing instructions intended for this kind of usage?
It sounds to me like the simplest approach would be to use regular nodes, but in a different xml namespace - i.e.
<foo standardAttrubute="abc" myData:customAttribute="def">
<standardElement>ghi</standardElement >
<myData:customElement>jkl</myData:customElement>
</foo>
(where myData is an xmlns alias for the namespace-uri)
In many cases, readers are only checking for data in their namespace (or the default/blank namespace) - values in custom namespaces are generally skipped.
To get pack the original xml, one simple approach would be to run it through an xslt that only respects the default/original namespace.
XNamespace myData = XNamespace.Get("http://mycustomdata/");
XElement el = new XElement("foo",
new XAttribute(XNamespace.Xmlns + "myData", myData.NamespaceName),
new XAttribute("standardAttribute", "abc"),
new XAttribute(myData + "customAttribute", "def"),
new XElement("standardElement", "ghi"),
new XElement(myData + "customAttribute", "jkl"));
string s = el.ToString();
To remove such data from an XElement, perhaps:
static void Strip(XElement el, XNamespace ns) {
List<XElement> remove = new List<XElement>();
foreach (XElement child in el.Elements()) {
if (child.Name.Namespace == ns) {
remove.Add(child);
} else {
Strip(child, ns);
}
}
remove.ForEach(child => child.Remove());
foreach (XAttribute child in
(from a in el.Attributes()
where a.Name.Namespace == ns
select a).ToList()) {
child.Remove();
}
}
The original question used the word "Serialize" but then also mentioned XElement and annotation. To me these are two different things.
If you really want to use the XmlSerializer:
What I would do is use XmlAttributeOverrides, to differentiate the serialization.
With the XmlAttributeOverrides you can programmatically, at runtime, override the xml serialization attributes that decorate your types.
Within your type, you can have a field/property that is intended to hold the annotation/documentation. Decorate that with XmlIgnore. Then, create one instance of the XmlSerializer that accepts no overrides. The annotation will not be serialized or de-serialized. Create another instance of the XmlSerializer for that type, using an XmlAttributeOverrides object. Specify an override for the XmlIgnore'd property (use XmlElementAttribute), as well as overrides for any attributes on any of the other members (use XmlIgnore=true).
Serialize the instance twice, one with each serializer.
Edit: here's the code:
public class DTO
{
[XmlIgnore]
public string additionalInformation;
[XmlElement(Order=1)]
public DateTime stamp;
[XmlElement(Order=2)]
public string name;
[XmlElement(Order=3)]
public double value;
[XmlElement(Order=4)]
public int index;
}
public class OverridesDemo
{
public void Run()
{
DTO dto = new DTO
{
additionalInformation = "This will bbe serialized separately",
stamp = DateTime.UtcNow,
name = "Marley",
value = 72.34,
index = 7
};
// ---------------------------------------------------------------
// 1. serialize normally
// this will allow us to omit the xmlns:xsi namespace
var ns = new XmlSerializerNamespaces();
ns.Add( "", "" );
XmlSerializer s1 = new XmlSerializer(typeof(DTO));
var builder = new System.Text.StringBuilder();
var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
Console.WriteLine("\nSerialize using the in-line attributes: ");
using ( XmlWriter writer = XmlWriter.Create(builder, settings))
{
s1.Serialize(writer, dto, ns);
}
Console.WriteLine("{0}",builder.ToString());
Console.WriteLine("\n");
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// 2. serialize with attribute overrides
// use a non-empty default namespace
ns = new XmlSerializerNamespaces();
string myns = "urn:www.example.org";
ns.Add( "", myns);
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();
// override the (implicit) XmlRoot attribute
XmlRootAttribute attr1 = new XmlRootAttribute
{
Namespace = myns,
ElementName = "DTO-Annotations",
};
attrs.XmlRoot = attr1;
overrides.Add(typeof(DTO), attrs);
// "un-ignore" the first property
// define an XmlElement attribute, for a type of "String", with no namespace
var a2 = new XmlElementAttribute(typeof(String)) { ElementName="note", Namespace = myns };
// add that XmlElement attribute to the 2nd bunch of attributes
attrs = new XmlAttributes();
attrs.XmlElements.Add(a2);
attrs.XmlIgnore = false;
// add that bunch of attributes to the container for the type, and
// specifically apply that bunch to the "additionalInformation" property
// on the type.
overrides.Add(typeof(DTO), "additionalInformation", attrs);
// now, XmlIgnore all the other properties
attrs = new XmlAttributes();
attrs.XmlIgnore = true;
overrides.Add(typeof(DTO), "stamp", attrs);
overrides.Add(typeof(DTO), "name", attrs);
overrides.Add(typeof(DTO), "value", attrs);
overrides.Add(typeof(DTO), "index", attrs);
// create a serializer using those xml attribute overrides
XmlSerializer s2 = new XmlSerializer(typeof(DTO), overrides);
Console.WriteLine("\nSerialize using the override attributes: ");
builder.Length = 0;
using ( XmlWriter writer = XmlWriter.Create(builder, settings))
{
s2.Serialize(writer, dto, ns);
}
Console.WriteLine("{0}",builder.ToString());
Console.WriteLine("\n");
// ---------------------------------------------------------------
}
}
output, using the in-line attributes:
<DTO>
<stamp>2009-06-30T02:17:35.918Z</stamp>
<name>Marley</name>
<value>72.34</value>
<index>7</index>
</DTO>
output, using the override attributes:
<DTO-Annotations xmlns="urn:www.example.org">
<note>This will bbe serialized separately</note>
</DTO-Annotations>
Related
When setting the InnerXml of an XmlElement having a default namespace, all tags without explicit namespaces are parsed as if they have xmlns="", instead of inheriting that XmlElement's default namespace (which is what happens when parsing a real XML document).
My question is: how to parse a complex XML string as a document fragment and assign it to an XmlElement, and inheriting the target XmlElement's namespace prefixes, default namespace, etc. when parsing that string?
Disclaimer:
I am totally aware of what XML namespaces are and what is the exact behavior of XmlElement.InnerXml regarding to XML namespaces. I'm not asking why XmlElement.InnerXml is doing what it currently does, or whether such behavior is good or bad. I'm asking if I can change this behavior or use some other techniques to achieve what I've described above.
I'm implementing some kind of XML template system, which allows users to insert some rather complex XML strings as fragments into another XML document. It will be insane to require users to always use explicit namespaces (the overhead of writing redundant namespace declarations can easily defeat the benefit of templating). I want a method to parse them and insert the resulting fragments into the main document as if they are literally copy-and-pasted into the target.
I'm aware that it is possible to preserve the default namespaces with pure DOM operations (like XmlDocument.CreateElement), but I don't want to manually implement an XML parser and convert XML strings into DOM operations.
I also don't want to do "serialize the whole XML document, do string manipulation, and parse it back" kind of things.
Is it possible?
As mentioned in this comment by Martin Honnen as well as this answer by Gideon Engelberth to Can I use predefined namespaces when loading an XDocument?, you can use an XmlParserContext to predefine a value for a namespace (including the default namespace) when parsing XML via XmlReader. Using an XmlReader configured with an appropriate context, you can load your inner XML directly into your XmlElement and and inherit any required namespaces from its scope.
To inherit just the default namespace, create the following extension methods:
public static partial class XmlNodeExtensions
{
public static void SetInnerXmlAndInheritDefaultNamespace(this XmlElement xmlElement, string innerXml)
{
using (var textReader = new StringReader(innerXml))
xmlElement.SetInnerXmlAndInheritDefaultNamespace(textReader);
}
public static void SetInnerXmlAndInheritDefaultNamespace(this XmlElement xmlElement, TextReader innerTextReader)
{
XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
mgr.AddNamespace("", xmlElement.GetNamespaceOfPrefix(""));
XmlParserContext ctx = new XmlParserContext(null, mgr, null, XmlSpace.Default);
using (var reader = XmlReader.Create(innerTextReader, new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = false }, ctx))
using (var writer = xmlElement.CreateNavigator().AppendChild())
{
writer.WriteNode(reader, true);
}
}
}
To inherit all namespaces (the requirements in your question aren't entirely clear), create the following:
public static partial class XmlNodeExtensions
{
public static void SetInnerXmlAndInheritNamespaces(this XmlElement xmlElement, string innerXml)
{
using (var textReader = new StringReader(innerXml))
xmlElement.SetInnerXmlAndInheritNamespaces(textReader);
}
public static void SetInnerXmlAndInheritNamespaces(this XmlElement xmlElement, TextReader innerTextReader)
{
XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
var navigator = xmlElement.CreateNavigator();
foreach (var pair in navigator.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml))
mgr.AddNamespace(pair.Key, pair.Value);
XmlParserContext ctx = new XmlParserContext(null, mgr, null, XmlSpace.Default);
using (var writer = navigator.AppendChild())
using (var reader = XmlReader.Create(innerTextReader, new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = false }, ctx))
{
writer.WriteNode(reader, true);
}
}
}
Assuming xmlElement is the existing XmlElement to which you want to insert an innerXml string, you can do:
xmlElement.SetInnerXmlAndInheritDefaultNamespace(innerXml);
E.g. if your XML Document looks like:
<Root xmlns="defaultNameSpace" xmlns:d="dataNodeNamespace"><d:Data></d:Data></Root>
And you add the following XML to <d:Data>:
<ElementToAdd Id="10101"><InnerValue>my inner value</InnerValue></ElementToAdd><ElementToAdd Id="20202"><InnerValue>another inner value</InnerValue></ElementToAdd>
The result will be:
<Root xmlns="defaultNameSpace" xmlns:d="dataNodeNamespace">
<d:Data>
<ElementToAdd Id="10101">
<InnerValue>my inner value</InnerValue>
</ElementToAdd>
<ElementToAdd Id="20202">
<InnerValue>another inner value</InnerValue>
</ElementToAdd>
</d:Data>
</Root>
Demo fiddle here and here.
If I Serialize a derived class, I cannot Deserialize the base class even with XmlInclude attributes. It fails with " was not expected." If I use the same serializer instance that I used to serialize the data, it works. If I create a new serializer (like if it was in another process), it fails.
[Serializable]
[XmlInclude(typeof(D))]
public class B
{
public string x { get; set; }
}
public class D : B
{
public string y { get; set; }
}
... code snippet ....
D x = new D();
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(D));
serializer.Serialize(ms, x);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
string responseBuffer = Encoding.ASCII.GetString(ms.GetBuffer());
ms.Seek(0, SeekOrigin.Begin);
serializer = new XmlSerializer(typeof(B));
B x2 = (B)serializer.Deserialize(ms);
}
As I noted before, if I used the original serializer (typeof(D)) it works. If I recreate the serializer (typeof(B)).
Thanks in advance for your help,
You have to create serializer for base type only when you want to deserialize any/all of the derived classes automatically through XmlInclude attribute. Your code isn't working because you start off with creating XmlSerializer serializer = new XmlSerializer(typeof(D)); which is a serializer for child type, and has no context of base class.
Later when you try to deserialize this child xml by using Serializer of type B, it fails because generated serialized Xml does not have context of base type.
You have to create Serializer of type B so that generated Xml has the attribute xsi:type which instructs XmlSerializer which child type to instantiate during deserialization.
Change your code to this:
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(B));
serializer.Serialize(ms, x);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
string responseBuffer = Encoding.ASCII.GetString(ms.GetBuffer());
ms.Seek(0, SeekOrigin.Begin);
serializer = new XmlSerializer(typeof(B));
B x2 = (B)serializer.Deserialize(ms);
}
Xml from your earlier code would look like:
<D xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
Whereas, code posted above will generate following Xml:
<B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="D" />
Note: the Node itself is now B, but additional attribute xsi:type="D" tells the XmlSerializer what is the actual type. This works due to your inclusion of XmlInclude attribute in the parent node.
Also, for generating/serializing xml to string MemoryStream type is not needed. Conversion through ASCII GetString is not ideal. You can use StringWriter and StringReader
You can simplify it
D x = new D();
string xml;
using(var sw = new StringWriter())
{
var serializer = new XmlSerializer(typeof(B));
serializer.Serialize(sw, x);
xml = sw.ToString();
}
B x2;
using(var sr = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(B));
x2 = serializer.Deserialize(sr) as B;
}
// if you check instance of x2 in debug, it will be of type D
You are going to need to let the XmlSerializer know about all the types that need to be deserialized.
Create the XmlSerializer with the base class type, then add the other types
Example:
var serializer = new XmlSerializer(typeof(B), new Type[] { typeof(D), typeof(C) });
I have a class InputConfig which contains a List<IncludeExcludeRule>:
public class InputConfig
{
// The rest of the class omitted
private List<IncludeExcludeRule> includeExcludeRules;
public List<IncludeExcludeRule> IncludeExcludeRules
{
get { return includeExcludeRules; }
set { includeExcludeRules = value; }
}
}
public class IncludeExcludeRule
{
// Other members omitted
private int idx;
private string function;
public int Idx
{
get { return idx; }
set { idx = value; }
}
public string Function
{
get { return function; }
set { function = value; }
}
}
Using ...
FileStream fs = new FileStream(path, FileMode.Create);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(InputConfig));
xmlSerializer.Serialize(fs, this);
fs.Close();
... and ...
StreamReader sr = new StreamReader(path);
XmlSerializer reader = new XmlSerializer(typeof(InputConfig));
InputConfig inputConfig = (InputConfig)reader.Deserialize(sr);
It works like a champ! Easy stuff, except that I need to preserve whitespace in the member function when deserializing. The generated XML file demonstrates that the whitespace was preserved when serializing, but it is lost on deserializing.
<IncludeExcludeRules>
<IncludeExcludeRule>
<Idx>17</Idx>
<Name>LIEN</Name>
<Operation>E =</Operation>
<Function> </Function>
</IncludeExcludeRule>
</IncludeExcludeRules>
The MSDN documentation for XmlAttributeAttribute seems to address this very issue under the header Remarks, yet I don't understand how to put it to use. It provides this example:
// Set this to 'default' or 'preserve'.
[XmlAttribute("space",
Namespace = "http://www.w3.org/XML/1998/namespace")]
public string Space
Huh? Set what to 'default' or 'preserve'? I'm sure I'm close, but this just isn't making sense. I have to think there's just a single line XmlAttribute to insert in the class before the member to preserve whitespace on deserialize.
There are many instances of similar questions here and elsewhere, but they all seem to involve the use of XmlReader and XmlDocument, or mucking about with individual nodes and such. I'd like to avoid that depth.
To preserve all whitespace during XML deserialization, simply create and use an XmlReader:
StreamReader sr = new StreamReader(path);
XmlReader xr = XmlReader.Create(sr);
XmlSerializer reader = new XmlSerializer(typeof(InputConfig));
InputConfig inputConfig = (InputConfig)reader.Deserialize(xr);
Unlike XmlSerializer.Deserialize(XmlReader), XmlSerializer.Deserialize(TextReader) preserves only significant whitespace marked by the xml:space="preserve" attribute.
The cryptic documentation means that you need to specify an additional field with the [XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")] whose value is default or preserve. XmlAttribute controls the name of the generated attribute for a field or property. The attribute's value is the field's value.
For example, this class:
public class Group
{
[XmlAttribute (Namespace = "http://www.cpandl.com")]
public string GroupName;
[XmlAttribute(DataType = "base64Binary")]
public Byte [] GroupNumber;
[XmlAttribute(DataType = "date", AttributeName = "CreationDate")]
public DateTime Today;
[XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")]
public string Space ="preserve";
}
Will be serialized to:
<?xml version="1.0" encoding="utf-16"?>
<Group xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
d1p1:GroupName=".NET"
GroupNumber="ZDI="
CreationDate="2001-01-10"
xml:space="preserve"
xmlns:d1p1="http://www.cpandl.com" />
I believe the part you are missing is to add the xml:space="preserve" to the field, e.g.:
<Function xml:space="preserve"> </Function>
For more details, here is the relevant section in the XML Specification
With annotation in the class definition, according to the MSDN blog it should be:
[XmlAttribute("space=preserve")]
but I remember it being
[XmlAttribute("xml:space=preserve")]
Michael Liu's answer above worked for me, but with one caveat. I would have commented on his answer, but my "reputation" is not adequate enough.
I found that using XmlReader did not fully fix the issue, and the reason for this is that the .net property in question had the attribute:
XmlText(DataType="normalizedString")
To rectify this I found that adding the additional attribute worked:
[XmlAttribute("xml:space=preserve")]
Obviously, if you have no control over the .net class then you have a problem.
I'm trying to modify an attribute of an XML string using Json in C#. Currently I'm doing the following:
XmlDocument serializedFormXml = new XmlDocument();
serializedFormXml.LoadXml(mySerializedForm);
string formJsonString = JsonConvert.SerializeXmlNode(serializedFormXml, Newtonsoft.Json.Formatting.None, true);
JObject formJsonObj = JObject.Parse(formJsonString);
formJsonObj["#code"] = "myNewValue";
var xml = JsonConvert.DeserializeXmlNode(formJsonObj.ToString()).ToString();
When I do this I get get an exception on the last line:
Unable to cast object of type 'Newtonsoft.Json.Converters.XmlDocumentWrapper' to type 'Newtonsoft.Json.Converters.IXmlElement'
Any ideas what I'm doing wrong and how I can fix modify my form attribute "code"?
This is the XML I'm using:
<Form code="XYZ">
<Info>Data</Info>
.....
Thanks!
That's going to be way, way easier with Linq-to-XML:
var doc = XDocument.Parse(mySerializedForm);
doc.Root.SetAttributeValue(doc.Root.Name.Namespace + "code", "myNewValue");
var xml = doc.ToString();
This drops the XML declaration. If you need the XML declaration included, you can use the following extension method:
public static class XObjectExtensions
{
public static string ToXml(this XDocument xDoc)
{
using (var writer = new StringWriter())
{
xDoc.Save(writer);
return writer.ToString();
}
}
}
And then write:
var xml = doc.ToXml();
If specifically you need to make the encoding string say "UTF-8", use Utf8StringWriter from this answer.
Update
The reason you code fails is that you stripped the XML root element name away when you converted to json by passing true here:
string formJsonString = JsonConvert.SerializeXmlNode(serializedFormXml, Newtonsoft.Json.Formatting.None, true);
Thus you need to add it back when converting back:
var xml = JsonConvert.DeserializeXmlNode(formJsonObj.ToString(), serializedFormXml.DocumentElement.Name).ToString();
Consider generating the following XML structure, which has 2 prefixed namespaces:
XNamespace ns1 = "http://www.namespace.org/ns1";
const string prefix1 = "w1";
XNamespace ns2 = "http://www.namespace.org/ns2";
const string prefix2 = "w2";
var root =
new XElement(ns1 + "root",
new XElement(ns1 + "E1"
, new XAttribute(ns1 + "attr1", "value1")
, new XAttribute(ns2 + "attr2", "value2"))
, new XAttribute(XNamespace.Xmlns + prefix2, ns2)
, new XAttribute(XNamespace.Xmlns + prefix1, ns1)
);
It generates the following XML result (which is fine):
<w1:root xmlns:w2="http://www.namespace.org/ns2" xmlns:w1="http://www.namespace.org/ns1">
<w1:E1 w1:attr1="value1" w2:attr2="value2" />
</w1:root>
The problem arises when I try to change ns1 from a prefixed namespace to a default namespace by commenting out its XML declaration, as in:
var root =
new XElement(ns1 + "root",
new XElement(ns1 + "E1"
, new XAttribute(ns1 + "attr1", "value1")
, new XAttribute(ns2 + "attr2", "value2"))
, new XAttribute(XNamespace.Xmlns + prefix2, ns2)
//, new XAttribute(XNamespace.Xmlns + prefix1, ns1)
);
which produces:
<root xmlns:w2="http://www.namespace.org/ns2" xmlns="http://www.namespace.org/ns1">
<E1 p3:attr1="value1" w2:attr2="value2" xmlns:p3="http://www.namespace.org/ns1" />
</root>
Note the duplicate namespace definitions in root and E1 and attributes prefixed as p3 under E1. How can I avoid this from happening? How can I force declaration of default namespace in the root element?
Related Questions
I studied this question: How to set the default XML namespace for an XDocument
But the proposed answer replaces namespace for elements without any namespace defined. In my samples the elements and attributes already have their namespaces correctly set.
What I have tried
Based on too many trial and errors, it seems to me that attributes which are not directly under the root node, where the attribute and its direct parent element both have the same namespace as the default namespace; the namespace for the attribute needs to be removed!!!
Based on this I defined the following extension method which traverses all the elements of the resulting XML and performs the above. In all my samples thus far this extension method fixed the problem successfully, but it doesn't necessarily mean that somebody can't produce a failing example for it:
public static void FixDefaultXmlNamespace(this XElement xelem, XNamespace ns)
{
if(xelem.Parent != null && xelem.Name.Namespace == ns)
{
if(xelem.Attributes().Any(x => x.Name.Namespace == ns))
{
var attrs = xelem.Attributes().ToArray();
for (int i = 0; i < attrs.Length; i++)
{
var attr = attrs[i];
if (attr.Name.Namespace == ns)
{
attrs[i] = new XAttribute(attr.Name.LocalName, attr.Value);
}
}
xelem.ReplaceAttributes(attrs);
}
}
foreach (var elem in xelem.Elements())
elem.FixDefaultXmlNamespace(ns);
}
This extension method produces the following XML for our question, which is what I desire:
<root xmlns:w2="http://www.namespace.org/ns2" xmlns="http://www.namespace.org/ns1">
<E1 attr1="value1" w2:attr2="value2" />
</root>
However I don't like this solution, mainly because it is expensive. I feel I'm missing a small setting somewhere. Any ideas?
Quoting from here:
An attribute is not considered a child of its parent element. An attribute never inherits the namespace of its parent element. For that reason an attribute is only in a namespace if it has a proper namespace prefix. An attribute can never be in a default namespace.
and here:
A default namespace declaration applies to all unprefixed element names within its scope. Default namespace declarations do not apply directly to attribute names; the interpretation of unprefixed attributes is determined by the element on which they appear.
It seems that this odd behavior of LINQ-to-XML is rooted in standards. Therefore whenever adding a new attribute its namespace must be compared against the parents' default namespace which is active in its scope. I use this extension method for adding attributes:
public static XAttribute AddAttributeNamespaceSafe(this XElement parent,
XName attrName, string attrValue, XNamespace documentDefaultNamespace)
{
if (newAttrName.Namespace == documentDefaultNamespace)
attrName = attrName.LocalName;
var newAttr = new XAttribute(attrName, attrValue);
parent.Add(newAttr);
return newAttr;
}
And use this extension method for retrieving attributes:
public static XAttribute GetAttributeNamespaceSafe(this XElement parent,
XName attrName, XNamespace documentDefaultNamespace)
{
if (attrName.Namespace == documentDefaultNamespace)
attrName = attrName.LocalName;
return parent.Attribute(attrName);
}
Alternatively, if you have the XML structure at hand and want to fix the namespaces already added to attributes, use the following extension method to fix this (which is slightly different from that outlined in the question):
public static void FixDefaultXmlNamespace(this XElement xelem,
XNamespace documentDefaultNamespace)
{
if (xelem.Attributes().Any(x => x.Name.Namespace == documentDefaultNamespace))
{
var attrs = xelem.Attributes().ToArray();
for (int i = 0; i < attrs.Length; i++)
{
var attr = attrs[i];
if (attr.Name.Namespace == documentDefaultNamespace)
{
attrs[i] = new XAttribute(attr.Name.LocalName, attr.Value);
}
}
xelem.ReplaceAttributes(attrs);
}
foreach (var elem in xelem.Elements())
elem.FixDefaultXmlNamespace(documentDefaultNamespace);
}
Note that you won't need to apply the above method, if you have used the first two methods upon adding and retrieving attributes.
I have found something for you, from the C# in a Nutshell book:
You can assign namespaces to attributes too. The main difference is that it always
requires a prefix. For instance:
<customer xmlns:nut="OReilly.Nutshell.CSharp" nut:id="123" />
Another difference is that an unqualified attribute always has an empty namespace:
it never inherits a default namespace from a parent element.
So given your desired output i have made a simple check.
var xml = #"<root xmlns:w2=""http://www.namespace.org/ns2"" xmlns=""http://www.namespace.org/ns1"">
<E1 attr1=""value1"" w2:attr2=""value2"" />
</root>";
var dom = XElement.Parse(xml);
var e1 = dom.Element(ns1 + "E1");
var attr2 = e1.Attribute(ns2 + "attr2");
var attr1 = e1.Attribute(ns1 + "attr1");
// attr1 is null !
var attrNoNS = e1.Attribute("attr1");
// attrNoNS is not null
So in short attr1 does not have default namespace, but has an empty namespace.
Is this you want? If yes, just create you attr1 without namespace...
new XAttribute("attr1", "value1")