Well, here is the final answer. I have used great Jimmy idea (which unfortunately is not complete itself) and complete recursion function to work properly.
Based on interface:
string RemoveAllNamespaces(string xmlDocument);
I represent here final clean and universal C# solution for removing XML namespaces:
//Implemented based on interface, not part of algorithm
public static string RemoveAllNamespaces(string xmlDocument)
{
XElement xmlDocumentWithoutNs = RemoveAllNamespaces(XElement.Parse(xmlDocument));
return xmlDocumentWithoutNs.ToString();
}
//Core recursion function
private static XElement RemoveAllNamespaces(XElement xmlDocument)
{
if (!xmlDocument.HasElements)
{
XElement xElement = new XElement(xmlDocument.Name.LocalName);
xElement.Value = xmlDocument.Value;
foreach (XAttribute attribute in xmlDocument.Attributes())
xElement.Add(attribute);
return xElement;
}
return new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(el => RemoveAllNamespaces(el)));
}
It's working 100%, but I have not tested it much so it may not cover some special cases... But it is good base to start.
Answer from Peter Stegnar on Stack OverflowWell, here is the final answer. I have used great Jimmy idea (which unfortunately is not complete itself) and complete recursion function to work properly.
Based on interface:
string RemoveAllNamespaces(string xmlDocument);
I represent here final clean and universal C# solution for removing XML namespaces:
//Implemented based on interface, not part of algorithm
public static string RemoveAllNamespaces(string xmlDocument)
{
XElement xmlDocumentWithoutNs = RemoveAllNamespaces(XElement.Parse(xmlDocument));
return xmlDocumentWithoutNs.ToString();
}
//Core recursion function
private static XElement RemoveAllNamespaces(XElement xmlDocument)
{
if (!xmlDocument.HasElements)
{
XElement xElement = new XElement(xmlDocument.Name.LocalName);
xElement.Value = xmlDocument.Value;
foreach (XAttribute attribute in xmlDocument.Attributes())
xElement.Add(attribute);
return xElement;
}
return new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(el => RemoveAllNamespaces(el)));
}
It's working 100%, but I have not tested it much so it may not cover some special cases... But it is good base to start.
The tagged most useful answer has two flaws:
- It ignores attributes
- It doesn't work with "mixed mode" elements
Here is my take on this:
public static XElement RemoveAllNamespaces(XElement e)
{
return new XElement(e.Name.LocalName,
(from n in e.Nodes()
select ((n is XElement) ? RemoveAllNamespaces(n as XElement) : n)),
(e.HasAttributes) ?
(from a in e.Attributes()
where (!a.IsNamespaceDeclaration)
select new XAttribute(a.Name.LocalName, a.Value)) : null);
}
Sample code here.
C# XML without declaration and namespace
How to remove System.Xml.XmlElement (not XElement) Outerxml xmlns?
c# - Remove xmlns attribute from xml - Stack Overflow
c# - How to remove xmlns attribute of a node other than root in an XDocument? - Stack Overflow
@Markus Freitag To override the behaviour of XmlSerializer you need XmlWriterSettings for override or remove XML declaration and XmlSerializerNamespaces for override namespace:
public static string ToXML(this T obj)
{
// Remove Declaration
var settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true
};
// Remove Namespace
var ns = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(writer, obj, ns);
return stream.ToString();
}
}
See full example here.
The most natural and easy way to implement it is via XSLT.
Please find below an example of it.
The XSLT is generic. It will remove (1) XML prolog declaration, and (2) any namespace(s) from any XML file.
Input XML
XSLT
Output XML
c# to launch XSLT transformation
void Main()
{
const string SOURCEXMLFILE = @"e:\Temp\UniversalShipment.xml";
const string XSLTFILE = @"e:\Temp\RemoveNamespaces.xslt";
const string OUTPUTXMLFILE = @"e:\temp\UniversalShipment_output.xml";
try
{
XsltArgumentList xslArg = new XsltArgumentList();
using (XmlReader src = XmlReader.Create(SOURCEXMLFILE))
{
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XSLTFILE, new XsltSettings(true, true), new XmlUrlResolver());
XmlWriterSettings settings = xslt.OutputSettings.Clone();
settings.IndentChars = "\t";
// to remove BOM
settings.Encoding = new UTF8Encoding(false);
using (XmlWriter result = XmlWriter.Create(OUTPUTXMLFILE, settings))
{
xslt.Transform(src, xslArg, result, new XmlUrlResolver());
result.Close();
}
}
Console.WriteLine("File '{0}' has been generated.", OUTPUTXMLFILE);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I think the code below is what you want. You need to put each element into the right namespace, and remove any xmlns='' attributes for the affected elements. The latter part is required as otherwise LINQ to XML basically tries to leave you with an element of
Copy<!-- This would be invalid -->
<Firstelement xmlns="" xmlns="http://my.namespace">
Here's the code:
Copyusing System;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument doc = XDocument.Load("test.xml");
// All elements with an empty namespace...
foreach (var node in doc.Root.Descendants()
.Where(n => n.Name.NamespaceName == ""))
{
// Remove the xmlns='' attribute. Note the use of
// Attributes rather than Attribute, in case the
// attribute doesn't exist (which it might not if we'd
// created the document "manually" instead of loading
// it from a file.)
node.Attributes("xmlns").Remove();
// Inherit the parent namespace instead
node.Name = node.Parent.Name.Namespace + node.Name.LocalName;
}
Console.WriteLine(doc); // Or doc.Save(...)
}
}
There is no need to 'remove' the empty xmlns attribute. The whole reason that the empty xmlns attrib is added is because the namespace of your childnodes is empty (= '') and therefore differ from your root node. Adding the same namespace to your childs as well will solve this 'side-effect'.
CopyXNamespace xmlns = XNamespace.Get("http://my.namespace");
// wrong
var doc = new XElement(xmlns + "Root", new XElement("Firstelement"));
// gives:
<Root xmlns="http://my.namespace">
<Firstelement xmlns="" />
</Root>
// right
var doc = new XElement(xmlns + "Root", new XElement(xmlns + "Firstelement"));
// gives:
<Root xmlns="http://my.namespace">
<Firstelement />
</Root>
I'd like to expand upon the existing answers. Specifically, I'd like to refer to a common use-case for removing namespaces from an XElement, which is: to be able to use Linq queries in the usual way.
When a tag contains a namespace, one has to use this namespace as an XNamespace on every Linq query (as explained in this answer), so that with the OP's xml, it would be:
XNamespace ns = "http://www.blablabla.com/bla";
var element = xelement.Descendants(ns + "retEvent")).Single();
But usually, we don't want to use this namespace every time. So we need to remove it.
Now, @octaviocc's suggestion does remove the namespace attribute from a given element. However, the element name still contains that namespace, so that the usual Linq queries won't work.
Console.WriteLine(xelement.Attributes().Count()); // prints 1
xelement.Attributes().Where( e => e.IsNamespaceDeclaration).Remove();
Console.WriteLine(xelement.Attributes().Count()); // prints 0
Console.WriteLine(xelement.Name.Namespace); // prints "http://www.blablabla.com/bla"
XNamespace ns = "http://www.blablabla.com/bla";
var element1 = xelement.Descendants(ns + "retEvent")).SingleOrDefault(); // works
var element2 = xelement.Descendants("retEvent")).SingleOrDefault(); // returns null
Thus, we need to use @Sam Shiles suggestion, but it can be simplified (no need for recursion):
private static void RemoveAllNamespaces(XElement xElement)
{
foreach (var node in xElement.DescendantsAndSelf())
{
node.Name = node.Name.LocalName;
}
}
And if one needs to use an XDocument:
private static void RemoveAllNamespaces(XDocument xDoc)
{
foreach (var node in xDoc.Root.DescendantsAndSelf())
{
node.Name = node.Name.LocalName;
}
}
And now it works:
var element = xelement.Descendants("retEvent")).SingleOrDefault();
@octaviocc's answer did not work for me because xelement.Attributes() was empty, it wasn't returning the namespace as an attribute.
The following will remove the declaration in your case:
element.Name = element.Name.LocalName;
If you want to do it recursively for your element and all child elements use the following:
private static void RemoveAllNamespaces(XElement element)
{
element.Name = element.Name.LocalName;
foreach (var node in element.DescendantNodes())
{
var xElement = node as XElement;
if (xElement != null)
{
RemoveAllNamespaces(xElement);
}
}
}