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.
Many thanks to Ali Shah, this thread solved my problem perfectly! here's a C# conversion:
var dom = new XmlDocument();
dom.Load("C:/ExampleFITrade.xml));
var loaded = new XDocument();
if (dom.DocumentElement != null)
if( dom.DocumentElement.NamespaceURI != String.Empty)
{
dom.LoadXml(dom.OuterXml.Replace(dom.DocumentElement.NamespaceURI, ""));
dom.DocumentElement.RemoveAllAttributes();
loaded = XDocument.Parse(dom.OuterXml);
}
.NET DOM API doesn't support modifying element's namespace which is what you are essentially trying to do. So, in order to solve your problem you have to construct a new document one way or another. You can use the same .NET DOM API and create a new element without specifying its namespace. Alternatively, you can create an XSLT stylesheet that transforms your original "namespaced" document to a new one in which the elements will be not namespace-qualified.
@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);
}
}
It seems (although you didn't share it) that the movies XML file uses namespaces. So I assume that somewhere in that file (probably on the root element) you will have something like xmlns="mymovieurl". The important thing to realize is that each XML element and attribute is identified by a pair of strings. The local name (Movie, DVD, VHS, ...) and the namespace URI (empty, mymovieurl, ...). In your code above, since you didn't specify a default namespace all your elements are in the empty namespace (their namespace URI is an empty string). But your XML file to which you're adding these have its elements in some non-empty namespace (mymovieurl). In order to preserve the namespace for the element you're adding the code has to inject the xmlns="" attribute which marks that element and all its children to be in the empty namespace (just like you specified it in your code).
The solution depends on what you want to achieve. I assume that you want to add the elements into the namespace the rest of the file uses.
One simple way to do that is to add Imports
That means that all elements in your code without a prefix should belong to the namespace "mymovieurl". (Just change that to whatever namespace URI you movies file is using).
The Imports works just like namespace declaration in XML. Since you didn't show us the input XML or the namespace URI you want the output XML to be in, it's impossible to show the right code.
Assuming your file uses namespace URI "mynsuri", then you need to add something like:
Imports <xmlns="mynsuri">
That will make is such that all elements in your code which don't specify a prefix will belong to the "mynsuri". That is assuming you actually want the Movie element and its children to belong to that namespace.
Maybe a little more explicit way would be to:
Imports <xmlns:movie="mynsuri">
And then in your code you would create the elements like:
<movie:Movie><movie:MovieID> ...
The namespace declarations (the xmlns:movie='' attribute) will be added to the output during serialization automatically for you.
You need to register the prefix 'sd' using XmlNamespaceManager before you can use it in XPath query. Make sure you map the prefix to the default namespace http://g.h.i :
....
nsmgr = New XmlNamespaceManager(New XmlNameTable())
nsmgr.AddNamespace("sd", "http://g.h.i");
nodeList = xmlnode.DocumentElement.SelectNodes("/sd:ArrayOfAssessmentItemTimed/sd:AssessmentItemTimed", nsmgr)
....
XPath always treat element without prefix as an element with empty namespace, it doesn't have a notion of default namespace (only XML has), that's why you need to made up the prefix 'sd' here.
You can check How to: Declare and Use XML Namespace Prefixes for a simpler alternative.
Imports <xmlns="http://g.h.i">
and sample use:
'' Dim xDoc = System.Xml.Linq.XDocument.Load("file.xml")
Dim xDoc = <?xml version="1.0" encoding="utf-8"?>
<ArrayOfAssessmentItemTimed xmlns:xsi="http://a.b.c/"
xmlns:xsd="http://d.e.f" xmlns="http://g.h.i">
<AssessmentItemTimed>
<License>0577HJK</License>
<ChassisNumber>VF1KT1RG646667276</ChassisNumber>
<Kilometers>90957</Kilometers>
</AssessmentItemTimed>
<AssessmentItemTimed>
<License>0918HHM</License>
<ChassisNumber>VF1KT1RG646272649</ChassisNumber>
<Kilometers>158142</Kilometers>
</AssessmentItemTimed>
</ArrayOfAssessmentItemTimed>
For Each x In xDoc.<ArrayOfAssessmentItemTimed>.<AssessmentItemTimed>
MsgBox("License: " & vbTab & x.<License>.Value & vbLf &
"Chassis: " & vbTab & x.<ChassisNumber>.Value & vbLf &
"Kms: " & vbTab & x.<Kilometers>.Value & vbLf)
Next