Before posting this question I have tried all other solution on stack, but with no success.
I am unable to remove empty xmlns attribute from XElement using C#, I have tried the following Codes.
XElement.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
Another one which postted here
foreach (var attr in objXMl.Descendants().Attributes())
{
var elem = attr.Parent;
attr.Remove();
elem.Add(new XAttribute(attr.Name.LocalName, attr.Value));
}
Image This is you xml file
<Root xmlns="http://my.namespace">
<Firstelement xmlns="">
<RestOfTheDocument />
</Firstelement>
</Root>
This is you expect
<Root xmlns="http://my.namespace">
<Firstelement>
<RestOfTheDocument />
</Firstelement>
</Root>
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
<!-- This would be invalid -->
<Firstelement xmlns="" xmlns="http://my.namespace">
Here's the code:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument doc = XDocument.Load("test.xml");
foreach (var node in doc.Root.Descendants())
{
// If we have an empty namespace...
if (node.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(...)
}
}
If you add the namespace of the parent element to the element then the empty namespace tag disappears, as it isn't required because the element is in the same namespace.
here's a simpler way to do this. I believe it happens when you create separate xml segments and then join them to your document.
xDoc.Root.SaveDocument(savePath);
private static void SaveDocument(this XElement doc, string filePath)
{
foreach (var node in doc.Descendants())
{
if (node.Name.NamespaceName == "")
{
node.Name = ns + node.Name.LocalName;
}
}
using (var xw = XmlWriter.Create(filePath, new XmlWriterSettings
{
//OmitXmlDeclaration = true,
//Indent = true,
NamespaceHandling = NamespaceHandling.OmitDuplicates
}))
{
doc.Save(xw);
}
}
Did you try to get Xelement.Attribute by value to see if the element is the "xmlns" before removing.
Xelement.Attribute("xmlns").Value
Related
I'm trying to receive all elements with a given tag name, no matter where they are.
I have used the Descendants() method on my base element like this:
public static XElement GetModifiedDataSource(XElement rechnung, string parentElement, string newElementTag, string value = null)
{
foreach (var element in rechnung.Descendants(parentElement))
{
XElement newElement = new XElement(newElementTag);
if (value != null)
{
newElement.SetValue(value);
}
element.Add(newElement);
}
return rechnung;
}
For examlpe with string parentElement = "Produkt" I should receive multiple.
Sample:
<Schlussrechnung xmlns="http://someurl">
<Parameter>
<Version></Version>
</Parameter>
<Uebersicht>
<Kopf>
<Rechnungsempfaenger>
</Rechnungsempfaenger>
</Kopf>
<Detail>
</Detail>
</Uebersicht>
<AbrechnungsDetail>
<Messpunkt>
<Produktgruppe>
<Produkt>
HERE
</Produkt>
<Produkt>
AND HERE
</Produkt>
</Produktgruppe>
</Messpunkt>
</Schlussrechnung>
Steps to accomplish this :
Get all descendant elements in the element under rechnung XElement like
var descendants = rechnung.Descendants().
Get all Descendants with "LocalName" = "Produkt" using linq like
var getAllProdukt = descendants.ToList().Where(desc => desc.Name.LocalName == "Produkt").ToList();
This way you get a List of XElements with the tag "Produkt"
Your XML has default namespace which your target element inherits from the root element:
xmlns="http://someurl"
You can use combination of XNamespace and element's local-name to reference element in namespace:
// you can make `ns` as additional parameter of `GetModifiedDataSource`
XNamespace ns = "http://someurl";
foreach (var element in rechnung.Descendants(ns+parentElement))
{
....
}
Or, with the risk of getting element from the wrong namespace if any, you can ignore the namespace by looking only at the element's local-name:
foreach (var element in rechnung.Descendants().Where(o => o.Name.LocalName == parentElement)
{
....
}
How can I remove the xmlns namespace from a XElement?
I tried: attributes.remove, xElement.Name.NameSpace.Remove(0), etc, etc. No success.
My xml:
<event xmlns="http://www.blablabla.com/bla" version="1.00">
<retEvent version="1.00">
</retEvent>
</event>
How can I accomplish this?
#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);
}
}
}
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();
You could use IsNamespaceDeclaration to detect which attribute is a namespace
xelement.Attributes()
.Where( e => e.IsNamespaceDeclaration)
.Remove();
I have string XML. I loaded to XmlDocument. How can I add, edit and delete by simplest method by line, because I know only line which I should edit. It's better work wih XML like with string, or better work with XmlDocuments?
using System;
using System.Xml;
namespace testXMl
{
class Program
{
static void Main(string[] args)
{
string xml="<?xml version=\"1.0\"?>\r\n<application>\r\n<features>\r\n<test key=\"some_key\">\r\n</features>\r\n</application>";
XmlDocument xm = new XmlDocument();
xm.LoadXml(xml);
//Edit third line
//xm[3].EditName(featuresNew);
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<featuresNew>\r\n<test key=\"some_key\">\r\n</featuresNew>\r\n</application>"
//Add fourth line the Node
//xm[4].AddNode("FeatureNext");
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<FeatureNext>\r\n<FeatureNext>\r\n</features2>\r\n<test key=\"some_key\">\r\n</features>\r\n</application>"
//Delete sixth line
//xm[6].DeleteNode;
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<FeatureNext>\r\n<FeatureNext>\r\n</features2>\r\n</features>\r\n</application>"
}
}
}
Thanks, in advance.
You should always work with XDocument/XmlDocument objects. A key knowledge is the XPath query language.
This a quick XML crash course. Run with debugger and inspect the XML variable as you move on.
var xml = new XmlDocument();
xml.LoadXml(#"<?xml version='1.0'?>
<application>
<features>
<test key='some_key' />
</features>
</application>");
// Select an element to work with; I prefer to work with XmlElement instead of XmlNode
var test = (XmlElement) xml.SelectSingleNode("//test");
test.InnerText = "another";
test.SetAttribute("sample", "value");
var attr = test.GetAttribute("xyz"); // Works, even if that attribute doesn't exists
// Create a new element: you'll need to point where you should add a child element
var newElement = xml.CreateElement("newElement");
xml.SelectSingleNode("/application/features").AppendChild(newElement);
// You can also select elements by its position;
// in this example, take the second element inside "features" regardless its name
var delete = xml.SelectSingleNode("/application/features/*[2]");
// Trick part: if you found the element, navigate to its parent and remove the child
if (delete != null)
delete.ParentNode.RemoveChild(delete);
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")
I have some sets of xml in which I have to add namespace prefix on nodes which doesn't have any prefix.
I have written a code which works for all nodes other than root element.
Please point me to the thing which I can do change the root element prefix as well.
private void ReplaceFile(string xmlfile)
{
XmlDocument doc = new XmlDocument();
doc.Load(xmlfile);
var a = doc.CreateAttribute("xmlns:mailxml12tm");
a.Value = "http://idealliance.org/Specs/mailxml12.0a/mailxml_tm";
doc.DocumentElement.Attributes.Append(a);
doc.DocumentElement.Prefix = "mailxml12tm";
//foreach (XmlNode item in doc.SelectNodes("//*").Cast<XmlNode>().Where(item => item.Prefix.Length == 0))
//{
// item.Prefix = "mailxml12tm";
//}
doc.Save(xmlfile);
}
The xml file:
<DeliveryApptCreateRequest
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
p3:ApptType="Pallet" p3:PickupOrDelivery="Delivery"
p3:ShipperApptRequestID="4490B0C07355" p3:SchedulerCRID="6498874"
xmlns:p3="http://idealliance.org/Specs/mailxml12.0a/mailxml_defs">
<SubmitterTrackingID xmlns="http://idealliance.org/Specs/mailxml12.0a/mailxml_tm">2CAD3FBC71B1E1517021</SubmitterTrackingID>
<DestinationEntry xmlns="http://idealliance.org/Specs/mailxml12.0a/mailxml_tm">No</DestinationEntry>
<OneTimeAppt xmlns="http://idealliance.org/Specs/mailxml12.0a/mailxml_tm">
<PreferredAppt>2012-07-01T09:00:00Z</PreferredAppt>
</OneTimeAppt>
</DeliveryApptCreateRequest>
Have you seen this answer: https://stackoverflow.com/a/2255337/219344
by Jeff Sternal?
If you've already declared your namespace in the root node, you just
need to change the SetAttribute call to use the unprefixed attribute
name. So if your root node defines a namespace like this:
<People xmlns:s='http://niem.gov/niem/structures/2.0'>
You can do this and the attribute will pick up the prefix you've
already established:
// no prefix on the first argument - it will be rendered as //
s:id='ID_Person_01' TempElement.SetAttribute("id",
"http://niem.gov/niem/structures/2.0", "ID_Person_01");
If you have not yet declared the namespace (and its prefix), the
three-string XmlDocument.CreateAttribute overload will do it for you:
// Adds the declaration to your root node var attribute =
xmlDocToRef.CreateAttribute("s", "id",
"http://niem.gov/niem/structures/2.0"); attribute.InnerText =
"ID_Person_01" TempElement.SetAttributeNode(attribute);
You can use the following:
XmlDocument doc = new XmlDocument();
doc.LoadXml("<test xmlns='123'/>");
XmlElement e = doc.DocumentElement;
e.Prefix = "a";
Console.WriteLine(doc.InnerXml);
Output:
<a:test xmlns="123" xmlns:a="123" />
Founded in msdn.
Edit:
Since it does not work with your main element you can use .Replace("<", "<Prefixe:")); on your root element as a String. It's not pretty but it does the job.