C# - How to remove xmlns from XElement - c#

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();

Related

Nested descendants not found Linq XElement

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)
{
....
}

Unable to remove empty xmlns attribute from XElement using c#

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

How do I populate child elements in XML?

I have an xml (Foo.xml)template which is defined as follows:
<Parent:Request xmlns:user="http://xxx.com/">
<Parent:ElemA></Parent:ElemA>
<Parent:ChildNode>
<ElemB></ElemB>
<ElemC></ElemC>
</Parent:ChildNode>
<Parent:ParentName></Parent:ParentName>
</Parent:Request>
In my code, I am able to set the parent elements in the xmltemplate as follows:
public void FooA( MyDomainObject DoM)
{
private readonly XNamespace myNS = "http://ANameSpace.com/";
XElement fooRequestDoc = XElement.Load("Templates/Foo.xml");
XElement ElemA_El = fooRequestDoc.Descendants(myNS + "ElemA").FirstOrDefault();
ElemA_El.SetValue(DoM.ElemA);
}
In this case, if ElemA has a value of "ElementA", then the ElemA_El parameter would be set to this value.
My question is, how do I set a specific Child Note elements such as ElemB or ElemC?
I've tried using "Element" (since I understand it's used to retreive child elements) as follows:
XElement ElemB_El = fooRequestDoc.Element(myNS + "ChildNode");
But it's returning the entire block rather than just ElemB which I seek.
If you know the name of the tag you could do something like this:
XElement ElemB_El = (from node in fooRequestDoc.Descendants() where node.Name == myNS + "ElemB" select node).FirstOrDefault();
If you don't know the name of the tag you can take every Descendants of ChildNode like this:
var nodes = (from node in fooRequestDoc.Descendants(myNS + "ChildNode").Elements() select node).ToList();

How to cast XPathEvalute when it can be XElement or XAttribute?

So I have this code:
List<PriceDetail> prices =
(from item in xmlDoc.Descendants(shop.DescendantXName)
select new PriceDetail
{
Price = GetPrice(item.Element(shop.PriceXPath).Value),
GameVersion = GetGameVersion(((IEnumerable)item.XPathEvaluate(shop.TitleXPath)).Cast<XAttribute>().First<XAttribute>().Value, item.Element(shop.PlatformXPath).Value),
Shop = shop,
Link = item.Element(shop.LinkXPath).Value,
InStock = InStock(item.Element(shop.InStockXPath).Value)
}).ToList<PriceDetail>();
The problem I have is this code:
((IEnumerable)item.XPathEvaluate(shop.TitleXPath)).Cast<XAttribute>().First<XAttribute>().Value
Sometimes the object from XPathEvaluate could be XElement and then the casting doesn't work. So what I need is a Cast that works with both XAttribute and XElement.
Any suggestion?
Change your XPath expression (shop.TitleXPath) from:
someXPathExpression
to:
string(someXPathExpression)
Then you can simplify the code to just:
string result = item.XPathEvaluate(shop.TitleXPath) as string;
Complete working example:
using System;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
class TestXPath
{
static void Main(string[] args)
{
string xml1 =
#"<t>
<a b='attribute value'/>
<c>
<b>element value</b>
</c>
<e b='attribute value'/>
</t>";
string xml2 =
#"<t>
<c>
<b>element value</b>
</c>
<e b='attribute value'/>
</t>";
TextReader sr = new StringReader(xml1);
XDocument xdoc = XDocument.Load(sr, LoadOptions.None);
string result1 = xdoc.XPathEvaluate("string(/*/*/#b | /*/*/b)") as string;
TextReader sr2 = new StringReader(xml2);
XDocument xdoc2 = XDocument.Load(sr2, LoadOptions.None);
string result2 = xdoc2.XPathEvaluate("string(/*/*/#b | /*/*/b)") as string;
Console.WriteLine(result1);
Console.WriteLine(result2);
}
}
When this program is executed, the same XPath expression is applied on two different XML documents and, regardless of the fact that the argument to string() is an attribute the first time and is an element on the second, we get the correct results -- written to the Console:
attribute value
element value
Dimitre's solution returns empty string if the element is not found; we can't distinguish it from actual empty value. So I had to make this extension method that handles multiple results by XPath query and returns empty enumeration if nothing is found:
public static IEnumerable<string> GetXPathValues(this XNode node, string xpath)
{
foreach (XObject xObject in (IEnumerable)node.XPathEvaluate(xpath))
{
if (xObject is XElement)
yield return ((XElement)xObject).Value;
else if (xObject is XAttribute)
yield return ((XAttribute)xObject).Value;
}
}
XElement and XAttribute are both forms of XObject, so if a generic instance of type XObject will suffice for your needs, change your Cast<XAttribute> to Cast<XObject>.
If that won't work for your specific situation, you make use of OfType<XAttribute> or OfType<XElement> to filter for one or the other, but that would require two passes over the input, one to filter for XElement and a second pass to filter for XAttribute.
Before you make the cast you can check for the type using a code like this:
XElement e = item as XElement;
XAttribute a = item as XAttribute;
if(e != null)
//item is of type XElement
else
//item is of type XAttribute

XML Element and Namespace

I have the following method to parse XMLElements:
DisplayMessages(XElement root)
{
var items = root.Descendants("Item");
foreach (var item in items)
{
var name = item.Element("Name");
....
}
}
In debug mode, I can see the root as XML like this:
<ItemInfoList>
<ItemInfo>
<Item>
<a:Name>item 1</a:Name>
...
<Item>
...
and var name is null (I expect to get "item 1"). I tried to use "a:Name" but it caused exception("character : cannot be used in name"). I am not sure if I have to set namespace in root XElelement or not. All the xml node under root should be in the same namespace.
I am new to XElement. In my codes, item.Element("Name") will get its children node "Name"'s value value, is that right?
You need to use element names that include namespace. Try this:
static void DisplayMessages(XElement root)
{
var items = root.Descendants(root.GetDefaultNamespace() + "Item");
foreach (var item in items)
{
var name = item.Element(item.GetNamespaceOfPrefix("a") + "Name");
Console.WriteLine(name.Value);
}
}
Note that operator + is overloaded for XNamespace class in order to make code shorter: XNamespace.Addition Operator.
You do need to define the "a" namespace in the root element:
<Root a:xmlns="http:///someuri.com">
...
</Root>
Then you can select an element in a non-default namespace using this syntax in LINQ to XML:
XNamespace a = "http:///someuri.com"; // must match declaration in document
...
var name = item.Element(a + "Name");
EDIT:
To retrieve the default namespace:
XNamespace defaultNamespace = document.Root.GetDefaultNamespace();
// XNamespace.None is returned when default namespace is not explicitly declared
To find other namespace declarations:
var declarations = root.Attributes().Where(a => a.IsNamespaceDeclaration);
Note that namespaces can be declared on any element though so you would need to recursively search all elements in a document to find all namespace declarations. In practice though this is generally done in the root element, if you can control how the XML is generated then that won't be an issue.
You need to create XNames that have a non-null Namespace. To do so, you have to create an XNamespace, and add the element name, see Creating an XName in a Namespace.
If you work with XML data that contains namespaces, you need to declare these namespaces. (That's a general observation I made, even though it seems to make it difficult to "just have a look" on data you don't know).
You need to declare an XNamespace for your XElements, as in these MSDN samples: Element(), XName

Categories

Resources