Use Linq to Xml with Xml namespaces
LINQ to XML methods like Descendants
and Element
take an XName
as an argument. There is a conversion from string
to XName
that is happening automatically for you. You can fix this by adding an XNamespace
before the strings in your Descendants
and Element
calls. Watch out because you have 2 different namespaces at work.
string theXml =
@"true1";
//string theXml = @"true1";
XDocument xmlElements = XDocument.Parse( theXml );
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result" )
select new
{
TheBool = (bool) data.Element( nsa + "TheBool" ),
TheId = (int) data.Element( nsa + "TheId" ),
};
foreach ( var element in elements )
{
Console.WriteLine( element.TheBool );
Console.WriteLine( element.TheId );
}
Notice the use of ns in Descendants
and nsa in Elements
How to add namespace to xml using linq xml
When you write
XNamespace ns = "xsi";
That's creating an XNamespace
with a URI of just "xsi". That's not what you want. You want a namespace alias of xsi
... with the appropriate URI via an xmlns
attribute. So you want:
XDocument doc = XDocument.Parse(framedoc.ToString());
foreach (var node in doc.Descendants("document").ToList())
{
XNamespace ns = "http://www.w3.org/2001/XMLSchema-instance";
node.SetAttributeValue(XNamespace.Xmnls + "xsi", ns.NamespaceName);
node.SetAttributeValue(ns + "schema", "");
node.Name = "alto";
}
Or better, just set the alias at the root element:
XDocument doc = XDocument.Parse(framedoc.ToString());
XNamespace ns = "http://www.w3.org/2001/XMLSchema-instance";
doc.Root.SetAttributeValue(XNamespace.Xmlns + "xsi", ns.NamespaceName);
foreach (var node in doc.Descendants("document").ToList())
{
node.SetAttributeValue(ns + "schema", "");
node.Name = "alto";
}
Sample creating a document:
using System;
using System.Xml.Linq;
public class Test
{
static void Main()
{
XNamespace ns = "http://www.w3.org/2001/XMLSchema-instance";
XDocument doc = new XDocument(
new XElement("root",
new XAttribute(XNamespace.Xmlns + "xsi", ns.NamespaceName),
new XElement("element1", new XAttribute(ns + "schema", "s1")),
new XElement("element2", new XAttribute(ns + "schema", "s2"))
)
);
Console.WriteLine(doc);
}
}
Output:
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<element1 xsi:schema="s1" />
<element2 xsi:schema="s2" />
</root>
Use LINQ XML with a namespace
Namespaces in XML can be tricky. I've run into this problem myself a number of times. In all likelihood, the following will fix your problem:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants(doc.Root.Name.Namespace.GetName("Id"))
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
Basically, this just assumes the underlying element to have the same namespace as your root element. That's true in this case, but of course it doesn't have to be.
The right way, probably, is to do it explicitly. Now, granted, that kind of depends on how you're using this and your datasource, so make the decision for yourself, but that would require doing something more like this:
XDocument doc = XDocument.Load(filePath);
List<string> urlList = doc.Root.Descendants(System.Xml.Linq.XName.Get("Id", "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"))
.Select(x => (string)x)
.ToList();
Console.WriteLine(urlList.Count);
The cause for your problem was that the default behavior for XElement
, when not given an explicit namespace, is to assume no namespace. However, the default behavior for the XML spec is to assume the parent's namespace. In your case, those two were different, so it wasn't able to find the descendant.
Linq-to-XML get elements with namespace
Here is one way of ignoring default namespaces, if you really want to do that:
XDocument doc;
using (XmlTextReader xr = new XmlTextReader("input.xml"))
{
xr.Namespaces = false;
doc = XDocument.Load(xr);
}
foreach (XElement bar in doc.Descendants("bar"))
But I would suggest to accept the existence and importance of namespaces in XML and use the XName and XNamespace objects LINQ to XML provides to work with them.
LINQ to XML to with Multiple Namespaces in C#
As kjn pointed out, Name
and FirstName
elements are not in the profile
namesapce, they are in vCard
. And you have to reflect that in your code. You could also simplify your code a lot:
XNamespace profileNs = "http://www.SumURL.com/XML/profile/2.0#";
XNamespace vCardNs = "http://www.w3.org/2001/vcard-rdf/3.0#";
var a = (from level in xDoc.Descendants(profileNs + "personal")
select new
{
PeopleID = (string)level.Element(profileNs + "id"),
FirstName = (string)level.Elements(vCardNs + "Name")
.Elements(vCardNs + "FirstName")
.FirstOrDefault()
}).ToList();
Note that this will sometimes set the properties to null
, instead of string.Empty
, but I think that makes more sense if the data is not present.
Using LINQ to Parse XML Document with Namespace in Root Element
issue your problem is that you do not give a name to your namespace.
for example:<Message xmlns:name="http://www.myurl.com/Product" version="010" release="006">
.When you do not use a custom name for your namespace you must use the whole namespace as an identifier.
This is the name of the Body without the custom name
{{http://www.myurl.com/Product}Body}
and this is with the custom name{Body}
(Now you will be asking why the element body does not have the preceding{name:Body}
at its name this is happening because it does not have any naming implications)You can use the Null Conditional Operator.
For example:t.Element("Product").Element("Description").Element("Identification").Element("ID1")?.Value
Although this is not an answer LINQ basically is a way to do a complicated foreach. So the only thing that i would do its maybe use XPath instead of chaining elements that may in the feature any one of them may end up null.
I hope i helped. If any question comes up feel free to ask and i will try to explain.
Thanks,
EDIT:
When you want to find a xml node using Namespace you can use this kind of code. (If i did something wrong please spare me i'm C# Dev not Visual Basic)
Dim nameSpace As xDoc.Root.GetDefaultNamespace()?.NamespaceName
Dim body As xDoc.Descendants(XName.Get("Body", nameSpace))
Dim productXName As XName.Get("Product", nameSpace)
Dim descriptionXName As XName.Get("Description", nameSpace)
Dim identificationXName As XName.Get("Identification", nameSpace)
Dim id1XName As XName.Get("ID1", nameSpace)
Dim id2XName As XName.Get("ID2", nameSpace)
Dim typeXName As XName.Get("Type", nameSpace)
Dim nameXName As XName.Get("Name", nameSpace)
Dim query As From t In body
select New With {Key _
.ID1 = t.Element(productXName).Element(descriptionXName).Element(identificationXName).Element(id1XName)?.Value,
.ID2 = t.Element(productXName).Element(descriptionXName).Element(identificationXName).Element(id2XName)?.Value,
.Name = t.Element(productXName).Element(descriptionXName).Element(typeXName).Element(nameXName)?.Value}
' Updated Working VB.Net Code
Public Function GetProductDetail() As String
Dim xDoc As XDocument = Nothing
Dim returnValue As String = ""
Using xr As XmlReader = XmlReader.Create("C:\Automation\Example.xml")
xDoc = XDocument.Load(xr)
Dim xn = xDoc.Root.GetDefaultNamespace()?.NamespaceName
Dim body = xDoc.Descendants(XName.Get("Body", xn))
Dim productXName = XName.Get("Product", xn)
Dim descriptionXName = XName.Get("Description", xn)
Dim identificationXName = XName.Get("Identification", xn)
Dim id1XName = XName.Get("ID1", xn)
Dim id2XName = XName.Get("ID2", xn)
Dim typeXName = XName.Get("Type", xn)
Dim nameXName = XName.Get("Name", xn)
' Name1 May or Maynot exist in the XML file
Dim nameXName1 = XName.Get("Name1", xn)
' DT May or May not Exist in recived XML File
Dim query = From t In body
Select New With {Key _
.ID1 = t.Element(productXName).Element(descriptionXName).Element(identificationXName).Element(id1XName)?.Value,
.ID2 = t.Element(productXName).Element(descriptionXName).Element(identificationXName).Element(id2XName)?.Value,
.Name = t.Element(productXName).Element(descriptionXName).Element(typeXName).Element(nameXName)?.Value,
.DT = t.Element(productXName).Element(descriptionXName).Element(typeXName).Element(nameXName1)?.Value}
For Each item In query
returnValue = $"ID1: {item.ID1} | ID2: {item.ID2} | Name: {item.Name} | DT: {item.DT} "
Next
End Using
Return returnValue
End Function
Querying xml child elements with prefixed namespace using LINQ to XML
It turns out that I needed to combine Descendants() with Elements()
The following code acheived exactly what I was after:
var data = XElement.Parse(theXml);
XNamespace ns = "urn:com.foo.bar/GetResults";
var result = data.Descendants(ns + "Result")
.Elements()
.Select(x => new KeyValuePair<string, string>(x.Name.LocalName, x.Value))
.ToList();
How to Select an XML Node with a Namespace in LINQ to XML
Try this one
var doc = XDocument.Parse(data);
var names = new XmlNamespaceManager(new NameTable());
names.AddNamespace("emtpy", "http://qusetons.com/Cdc/AbcSchema.xsd");
Console.WriteLine(doc.XPathSelectElement("/emtpy:Abc/emtpy:xxx", names).Value);
Use Linq to Xml with Xml namespaces
LINQ to XML methods like Descendants
and Element
take an XName
as an argument. There is a conversion from string
to XName
that is happening automatically for you. You can fix this by adding an XNamespace
before the strings in your Descendants
and Element
calls. Watch out because you have 2 different namespaces at work.
string theXml =
@"true1";
//string theXml = @"true1";
XDocument xmlElements = XDocument.Parse( theXml );
XNamespace ns = "http://myvalue.com";
XNamespace nsa = "http://schemas.datacontract.org/2004/07/My.Namespace";
var elements = from data in xmlElements.Descendants( ns + "Result" )
select new
{
TheBool = (bool) data.Element( nsa + "TheBool" ),
TheId = (int) data.Element( nsa + "TheId" ),
};
foreach ( var element in elements )
{
Console.WriteLine( element.TheBool );
Console.WriteLine( element.TheId );
}
Notice the use of ns in Descendants
and nsa in Elements
Understand xml namespaces used with linq
Please try the following solution.
I saved your XML as a e:\Temp\Aschenauer.xml file:
<Section Name="Input" xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v4">
<Member Name="STARTTASTER" Datatype="Bool" Remanence="NonRetain" Accessibility="Public">
<AttributeList>
<BooleanAttribute Name="ExternalAccessible" SystemDefined="true">true</BooleanAttribute>
<BooleanAttribute Name="ExternalVisible" SystemDefined="true">true</BooleanAttribute>
<BooleanAttribute Name="ExternalWritable" SystemDefined="true">true</BooleanAttribute>
<BooleanAttribute Name="UserVisible" Informative="true" SystemDefined="true">true</BooleanAttribute>
<BooleanAttribute Name="UserReadOnly" Informative="true" SystemDefined="true">false</BooleanAttribute>
<BooleanAttribute Name="UserDeletable" Informative="true" SystemDefined="true">true</BooleanAttribute>
</AttributeList>
</Member>
</Section>
c#
void Main()
{
const string filename = @"e:\Temp\Aschenauer.xml";
XDocument xdoc = XDocument.Load(filename);
XNamespace ns = xdoc.Root.GetDefaultNamespace();
var NameAttr = xdoc.Descendants(ns + "Member")
.Attributes("Name").FirstOrDefault().Value;
Console.WriteLine($"Name='{NameAttr}'");
}
Output
Name='STARTTASTER'
Related Topics
Enum Tostring With User Friendly Strings
Ef Core Returns Null Relations Until Direct Access
Why and How to Avoid Event Handler Memory Leaks
Convert a Bitmap into a Byte Array
Raw SQL Query Without Dbset - Entity Framework Core
How to Reflect Over the Members of Dynamic Object
C# Getting the Path of %Appdata%
Error: "An Object Reference Is Required For the Non-Static Field, Method or Property..."
Metadataexception: Unable to Load the Specified Metadata Resource
How to Get the Datetime For the Start of the Week
What Does Question Mark and Dot Operator . Mean in C# 6.0
Call a Stored Procedure With Parameter in C#
Simple and Tested Online Regex Containing Regex Delimiters Does Not Work in C# Code
Converting String to Byte Array in C#