select childnode in xmlnode - c#

I have this result from an httpGet and i want to extract some values from it.
<?xml version="1.0" encoding="UTF-8"?>
<ns4:endpoint xmlns:ns4="identity.ers.ise.cisco.com" xmlns:ers="ers.ise.cisco.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema" description="Beskriv" id="eed9d9a0-5c8d-11ea-8778-
9e4294fe2fb6" name="1A:1A:1A:1A:1A:1A">
<link rel="self" href="https://10.100.10.10:9060/ers/config/endpoint/eed9d9a0-5c8d-11ea-8778-
9e4294fe2fb6" type="application/xml" />
<customAttributes>
<customAttributes>
<entry>
<key>Changed By API</key>
<value />
</entry>
<entry>
<key>TAG name</key>
<value>TagNavn</value>
</entry>
<entry>
<key>API Change Date</key>
<value>2020-03-02</value>
</entry>
<entry>
<key>API Changed By</key>
<value>Mot</value>
</entry>
</customAttributes>
</customAttributes>
<groupId>aa13bb40-8bff-11e6-996c-525400b48521</groupId>
<identityStore />
<identityStoreId />
<mac>1A:1A:1A:1A:1A:1A</mac>
<portalUser />
<profileId />
<staticGroupAssignment>true</staticGroupAssignment>
<staticProfileAssignment>false</staticProfileAssignment>
</ns4:endpoint>
I can extract the "name" and "description" by using xmlnode.value, but im not sure how to access my custom attributes such as <mac> and <groupid>
Are they regarded as child nodes?
Any help is appreciated.

Something as simple as (using XDocument class from System.Xml.Linq namespace):
// You don't need below line
// XDocument xml = XDocument.Load(#"path to txt file");
var macElement = xml.Descendants().Where(element => element.Name == "mac").FirstOrDefault();
// Similairly for groupid elemnt
var groupidElement = xml.Descendants().Where(element => element.Name == "groupid").FirstOrDefault();
if(macElement != null)
{
// do something with macElement.Value
}
if(groupidElement != null)
{
// do something with groupidElement.Value
}
Also call to xml.Descendants() is reduntant, so you could store it in variable like:
var descendants = xml.Descendants();
and reuse it.

Related

Selected XML elements to DataGrid using LINQ in C#

I have this XML response from REST service. I tried to show how it has deep element structure. As you can see XML has child elements wih the same name.
<list>
<message/>
<message/>
<message/>
<message>
<messageId>3</messageId>
<serverId>f5890d03-0bef-4704-a9de-9a1be64801c0</serverId>
<channelId>ffffffff-3656-4b6f-8fd1-253e99177b80</channelId>
<receivedDate>
<time>1509259172350</time>
<timezone>GMT+03:00</timezone>
</receivedDate>
<processed>true</processed>
<connectorMessages class="linked-hash-map">
<entry>
<int>0</int>
<connectorMessage>
<messageId>3</messageId>
<metaDataId>0</metaDataId>
<channelId>ffffffff-3656-4b6f-8fd1-253e99177b80</channelId>
<raw>
<encrypted>false</encrypted>
<channelId>ffffffff-3656-4b6f-8fd1-253e99177b80</channelId>
<messageId>3</messageId>
<metaDataId>0</metaDataId>
<contentType>RAW</contentType>
</raw>
<response>
<encrypted>false</encrypted>
<channelId>ffffffff-3656-4b6f-8fd1-253e99177b80</channelId>
<messageId>3</messageId>
<metaDataId>0</metaDataId>
</response>
<sourceMapContent>
<encrypted>false</encrypted>
<content class="java.util.Collections$UnmodifiableMap">
<m>
<entry>
<string>remoteAddress</string>
<string>127.0.0.1</string>
</entry>
<entry>
<string>destinationSet</string>
<linked-hash-set>
<int>1</int>
</linked-hash-set>
</entry>
</m>
</content>
</sourceMapContent>
<connectorMapContent>
<encrypted>false</encrypted>
<content class="map">
<entry>
<string>_source</string>
<string>Instance1</string>
</entry>
<entry>
<string>_version</string>
<string>2.5.1</string>
</entry>
</content>
</connectorMapContent>
<encrypted>false</encrypted>
<errorCode>0</errorCode>
</connectorMessage>
</entry>
</connectorMessages>
</message>
<message/>
</list>
I'm so new for C# and C# XML process. I spent my weekend to find a way to bind it to DataGridView. I want to take only 20 elements value from this XML response to my DataGridView.
I tried with DataTable/DataSet for this binding but I couldn't reach child elements which has the same name. It takes the first matching element.
var table = new DataTable("message");
table.Columns.Add("messageId", typeof(string));
table.Columns.Add("serverId", typeof(string));
table.Columns.Add("time", typeof(string));
table.Columns.Add("receivedDate", typeof(string));
table.Columns.Add("timezone", typeof(string));
table.ReadXml(new StringReader(contentSystemInfo));
dataGridView1.DataSource = table;
Than I tried with LINQ;
XDocument xmlDocument = XDocument.Parse(contentSystemInfo);
List<string> messageRAWContentList = xmlDocument.Root
.Elements("message")
.Elements("connectorMessages")
.Elements("entry")
.Elements("connectorMessage")
.Elements("raw")
.Elements("content")
.Select(x => (string)x)
.ToList();
Still could not reach child elements.
In an example I want to reach these element in every message element;
<list>
<message>
<messageId>3</messageId>
<serverId>f5890d03-0bef-4704-a9de-9a1be64801c0</serverId>
<receivedDate>
<time>1509259172350</time>
</receivedDate>
<connectorMessages class="linked-hash-map">
<entry>
<messageId>13</messageId>
<connectorMessage>
<raw>
<content>content</content>
</raw>
<response>
<content>content</content>
</response>
<sourceMapContent>
<content>
<m>
<entry>
<string>remoteAddress</string>
<string>127.0.0.1</string>
</entry>
<entry>
<string>destinationSet</string>
<linked-hash-set>
<int>1</int>
</linked-hash-set>
</entry>
</m>
</content>
</sourceMapContent>
</connectorMessage>
</entry>
</connectorMessages>
</message>
</list>
See if this works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("message").Select(x => new {
messageId = (int)x.Element("messageId"),
serverId = (string)x.Element("serverId"),
channelId = (string)x.Element("channelId"),
time = (long)x.Descendants("time").FirstOrDefault(),
connectorMessages = x.Descendants("connectorMessage").Select(y => new {
messageId = (int)y.Descendants("messageId").FirstOrDefault(),
raw = y.Elements("raw").Select(z => new {
encrypted = (bool)z.Element("encrypted"),
channelId = (string)z.Element("channelId"),
messagedId = (int)z.Element("messageId"),
metaDataId = (string)z.Element("metaDataId"),
contentType = (string)z.Element("contentType")
}).FirstOrDefault(),
response = y.Elements("response").Select(z => new {
encrypted = (bool)z.Element("encrypted"),
channelId = (string)z.Element("channelId"),
messagedId = (int)z.Element("messageId"),
metaDataId = (string)z.Element("metaDataId")
}).FirstOrDefault(),
entries = y.Descendants("entry").Select(z => new {
strings = z.Elements("string").Select(b => (string)b).ToList()
}).ToList()
}).ToList()
}).ToList();
}
}
}

moving to a node values in linq to xml C#?

I am trying get the email value under author node in C#. But nothing is coming. My Code is=
XDocument xDoc = XDocument.Parse("myxml");
var foos = from xelem in xDoc.Descendants("author")
select xelem.Element("email").Value;
XML which i am using is -
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:batch="http://schemas.google.com/gdata/batch"
xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:gd="http://schemas.google.com/g/2005"
xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
<id>yogeshcp13#gmail.com</id>
<updated>2015-02-09T04:03:31.220Z</updated>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"/>
<title type="text">Yogesh Adhikari's Contacts</title>
<link rel="alternate" type="text/html" href="https://www.google.com/"/>
<link rel="next" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/yogeshcs2003%40gmail.com/full?max-
results=1&start-index=2"/>
<author>
<name>Yogesh Adhikari</name>
<email>yogeshcp13#gmail.com</email>
</author>
<generator version="1.0" uri="http://www.google.com/m8/feeds">Contacts</generator>
<openSearch:totalResults>3099</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>1</openSearch:itemsPerPage>
</feed>
Can someone point out what is wrong?
Thanks
You need to specify the namespace along with the name when getting the descendants.
XDocument xDoc = XDocument.Parse("myxml");
string ns = xDoc.Root.Name.Namespace;
var foos = from xelem in xDoc.Descendants(ns + "author")
select xelem.Element(ns + "email").Value;
Alternatively, you can find your nodes by getting an enumeration over all descendants, then filtering by LocalName. If email is a node only within authors in your schema, you can also avoid the unnecessary step of drilling down from author nodes, and just find your email nodes directly:
var foos = xdoc.Descendants().Where(e => e.Name.LocalName == "email");
XDocument xDoc = XDocument.Load("myxml.xml");
var foos = xDoc.Descendants().Where(e => e.Name.LocalName == "email");
Console.WriteLine(foos.FirstOrDefault().Value);
Use Load method if you are refering to xml file else parse should be fine. Also make sure xml is along with your binaries' folder if not using specific path.

How to parse XML using c# and XDocument into an object when multiple elements have the same name?

I'm using SharePoint 2013's REST API to get XML to parse, but I'm having trouble parsing a certain chunk of XML. Here's a truncated example of some XML I might get back that I have no problem parsing:
<feed xml:base="http://server/DesktopApplications/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
<id>2dff4586-6b7d-4186-ae63-4d048f74a112</id>
<title />
<updated>2014-03-19T15:32:15Z</updated>
<entry m:etag=""19"">
<id>http://server/DesktopApplications/_api/Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')</id>
<category term="SP.List" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')" />
<title />
<updated>2014-03-19T15:32:15Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:BaseTemplate m:type="Edm.Int32">101</d:BaseTemplate>
<d:BaseType m:type="Edm.Int32">1</d:BaseType>
<d:Id m:type="Edm.Guid">0ac46109-38d9-4070-96b7-f90085b56f1e</d:Id>
<d:ListItemEntityTypeFullName>SP.Data.Shared_x0020_DocumentsItem</d:ListItemEntityTypeFullName>
<d:Title>Documents</d:Title>
</m:properties>
</content>
</entry>
...
</feed>
I can parse this just fine using XDocument with the following:
private static readonly XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
private static readonly XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
...
List<KLList> lists = doc.Descendants(m + "properties").Select(
list => new KLList()
{
Id = list.Element(d + "Id").Value,
Title = list.Element(d + "Title").Value,
ListItemEntityTypeFullName = list.Element(d + "ListItemEntityTypeFullName").Value,
BaseType = (BaseType)Convert.ToInt32(list.Element(d + "BaseType").Value),
ListTemplateType = (ListTemplateType)Convert.ToInt32(list.Element(d + "BaseTemplate").Value)
}).ToList();
When I expand my query to get the server relative url of the list's root folder, I get XML like this that I have trouble with:
<feed xml:base="http://server/DesktopApplications/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
<id>a01c22dc-a87f-4253-91d1-0a6ef8efa6d0</id>
<title />
<updated>2014-03-19T15:27:11Z</updated>
<entry m:etag=""19"">
<id>http://server/DesktopApplications/_api/Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')</id>
<category term="SP.List" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RootFolder" type="application/atom+xml;type=entry" title="RootFolder" href="Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')/RootFolder">
<m:inline>
<entry>
<id>http://server/DesktopApplications/_api/Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')/RootFolder</id>
<category term="SP.Folder" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'0ac46109-38d9-4070-96b7-f90085b56f1e')/RootFolder" />
<title />
<updated>2014-03-19T15:27:11Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:ServerRelativeUrl>/DesktopApplications/Shared Documents</d:ServerRelativeUrl>
</m:properties>
</content>
</entry>
</m:inline>
</link>
<title />
<updated>2014-03-19T15:27:11Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:BaseTemplate m:type="Edm.Int32">101</d:BaseTemplate>
<d:BaseType m:type="Edm.Int32">1</d:BaseType>
<d:Id m:type="Edm.Guid">0ac46109-38d9-4070-96b7-f90085b56f1e</d:Id>
<d:ListItemEntityTypeFullName>SP.Data.Shared_x0020_DocumentsItem</d:ListItemEntityTypeFullName>
<d:Title>Documents</d:Title>
</m:properties>
</content>
</entry>
...
</feed>
I tried using basically the same code because I want the ServerRelativeUrl to be on the same object:
List<KLList> lists = doc.Descendants(m + "properties").Select(
list => new KLList()
{
Id = list.Element(d + "Id").Value,
Title = list.Element(d + "Title").Value,
ListItemEntityTypeFullName = list.Element(d + "ListItemEntityTypeFullName").Value,
BaseType = (BaseType)Convert.ToInt32(list.Element(d + "BaseType").Value),
ListTemplateType = (ListTemplateType)Convert.ToInt32(list.Element(d + "BaseTemplate").Value),
RelativeUrl = list.Element(d + "ServerRelativeUrl").Value
}).ToList();
But this no longer works. The first thing doc.Descendants(m + "properties") returns is
<m:properties>
<d:ServerRelativeUrl>/DesktopApplications/Shared Documents</d:ServerRelativeUrl>
</m:properties>
so trying to set the other properties at the same time throws an object reference exception, because the other elements aren't here.
How can I parse this XML so that all of the values I want go into one object? I'd rather not have to make a separate call to get the root folder url for each list.
Update:
I posted an answer below, but I feel like there is a better way out there somewhere. Feel free to post an answer if it's better than mine and I'll mark yours as the answer.
I figured out a way to get me what I need but I feel like there has to be a better way...
private readonly XNamespace a = "http://www.w3.org/2005/Atom";
private readonly XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
private readonly XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
List<KLList> lists = doc.Descendants(a + "entry").Where(element => element.Attribute(m + "etag") != null).Select(
list => new KLList()
{
Id = list.Descendants(d + "Id").FirstOrDefault().Value,
Title = list.Descendants(d + "Title").FirstOrDefault().Value,
ListItemEntityTypeFullName = list.Descendants(d + "ListItemEntityTypeFullName").FirstOrDefault().Value,
BaseType = (BaseType)Convert.ToInt32(list.Descendants(d + "BaseType").FirstOrDefault().Value),
ListTemplateType = (ListTemplateType)Convert.ToInt32(list.Descendants(d + "BaseTemplate").FirstOrDefault().Value),
RelativeUrl = list.Descendants(d + "ServerRelativeUrl").FirstOrDefault().Value
}).ToList();
What you want to do is to check that the element exists before getting it's value because at the moment you are just assuming that they will always exist.
This has been asked before here (I haven't got enough reputation to post this as a comment):
Check if an element exists when parsing XML

Xelement - get elements

I'm loading an xml feed from youtube into xelement like so:
XElement element = XElement.Load(url);
This is fine and I get a document that looks like this:
<?xml version='1.0' encoding='UTF-8' ?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:opensearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:yt='http://gdata.youtube.com/schemas/2007'>
<id>http://gdata.youtube.com/feeds/api/users/id/uploads</id>
<updated>2014-01-07T11:43:08.269Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
<title type='text'>Uploads by id</title>
<logo>http://www.gstatic.com/youtube/img/logo.png</logo>
<link rel='related' type='application/atom+xml' href='https://gdata.youtube.com/feeds/api/users/stonemarketuk'/>
<link rel='alternate' type='text/html' href='https://www.youtube.com/channel/333/videos'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://gdata.youtube.com/feeds/api/users/id/uploads'/>
<link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://gdata.youtube.com/feeds/api/users/id/uploads/batch'/>
<link rel='self' type='application/atom+xml' href='https://gdata.youtube.com/feeds/api/users/id/uploads?start-index=1&max-results=25'/>
<link rel='next' type='application/atom+xml' href='https://gdata.youtube.com/feeds/api/users/id/uploads?start-index=26&max-results=25'/>
<author>
<name>StonemarketUK</name>
<uri>https://gdata.youtube.com/feeds/api/users/id</uri>
</author>
<generator version='2.1' uri='http://gdata.youtube.com'>YouTube data API</generator>
<openSearch:totalResults>30</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<entry>
....entry elements
</entry>
<entry>
....entry elements
</entry>
</feed>
How do I get the entry elements? I have tried the following:
var entries = element.Elements("entry");
var entries = element.Element("feed").Elements("entry");
var entries = element.Nodes().Elements("entry");
But none of these return anything
I also debugged and tried element.Elements().FirstOrDefault() but this returned null. element.Nodes().Count() returned 41 so should I be trying to get the nodes called entry?
All elements are in a namespace (xmlns='http://www.w3.org/2005/Atom'). You must declare the atom namespace and use it.
It's easy:
XNamespace atom = "http://www.w3.org/2005/Atom";
var xml = XElement.Load(url);
var entry = xml.Elements(atom + "entry");
etc.
An easy way to get the right Element name is using XName.Get(). If you only have one namespace, you could put it in a seperate function:
internal static XName GetXName(string name)
{
string atomNamespace = "http://www.w3.org/2005/Atom";
return XName.Get(name, atomNamespace);
}
Try the following
var entryList = element.Elements().Where(x => x.Name.LocalName == "entry");
The problem here is that every element without an explicit namespace is implicitly in the namespace "http://www.w3.org/2005/Atom". The LocalName property can still be used to access the simple name as shown above. Barring that you need to construct a proper XName element which includes this namespace in order to match the 'entry' nodes

Xpath/C#, getting data from multiple namespaces

So, in the XML below using this code sample, i can successfully grab HomeAddress and Name information for each entry from my XML. What if I also need to grab the ID (drivers license numberxxx) information from each entry?
EDIT: Updated XML sample for clarificaiton
Code Sample:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(responseFromServer);
XmlNamespaceManager xnsmgr = new XmlNamespaceManager(xDoc.NameTable);
xnsmgr.AddNamespace("ns1", "http://www.w3.org/2005/Atom");
xnsmgr.AddNamespace("ns2", "http://strange.com/ns/1.0/");
XmlNodeList xnlInsuredListMembers = xDoc.SelectNodes("//ns2:InsuredListMember", xnsmgr);
foreach (XmlNode xnMember in xnlInsuredListMembers)
{
XmlNode xnHomeAddress = xnMember.SelectSingleNode("ns2:HomeAddress", xnsmgr);
string sHomeAddress = xnHomeAddress.InnerText;
XmlNode xnName = xnMember.SelectSingleNode("ns2:Name", xnsmgr);
string sName = xnName.InnerText;
MessageBox.Show(sHomeAddress + sName);
}
XML Sample
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>someID</id>
<title type="text">Title</title>
<author>
<name>XML Author</name>
</author>
<updated>2010-10-25T20:05:30.267Z</updated>
<link href="currentURL"></link>
<link href="nextURL"></link>
<entry>
<id>Drivers License Numberxxx</id>
<content type="application/vnd.ctct+xml">
<InsuredListMember xmlns="http://strange.com/ns/1.0/">
<HomeAddress>123 anystreet</HomeAddress>
<Name>doe, somegal</Name>
</InsuredListMember>
</content>
</entry>
<entry>
<id>Drivers License Numberxxx</id>
<content type="application/vnd.ctct+xml">
<InsuredListMember xmlns="http://strange.com/ns/1.0/">
<HomeAddress>321 anystreet</HomeAddress>
<Name>doe, someguy</Name>
</InsuredListMember>
</content>
</entry>
</feed>
First, this //ns2:ContactListMember should be //ns2:InsuredListMember according to your input sample.
Second, if the context node is some ns2:InsuredListMember, the the id attribute is selected by this expresion: #id.
If you want the ns1:id child of ns1:entry for the given ns2:InsuredListMember, this XPath expression: ../../ns1:id

Categories