Use Linq to Xml With Xml Namespaces

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


  1. 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)

  2. You can use the Null Conditional Operator.

    For example: t.Element("Product").Element("Description").Element("Identification").Element("ID1")?.Value

  3. 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



Leave a reply



Submit