Get Last Element in C# using XElement - c#

I have a XML feed loaded in an XElement.
The structure is
<root>
<post></post>
<post></post>
<post></post>
<post></post>
.
.
.
.
<post></post>
</root>
I want to directly get the value of the Last post. How I do that using XElement in C#.
Thanks.

Or try this to get XElement:
XDocument doc = XDocument.Load("yourfile.xml");
XElement root = doc.Root;
Console.WriteLine(root.Elements("post").Last());

You can use LastNode property on root element:
XElement root = doc.Root;
XElement lastPost = (XElement)root.LastNode;

var doc = XDocument.Parse(xml);
var lastPost = doc.Descendants("post").Last();

Try this:
rootElement.Descendants().Last()
If you aren't sure there'll be any, you could also use LastOrDefault(). If there might be other elements besides within the , there's an overload of Descendants that will let you find just the posts you're looking for.

Try this
XDocument doc= XDocument.Load("path to xml");
var last=doc.Root.LastNode;

Related

XDocument get element that defines it's own namespace

As question states. I have a xml document (below) and I need to get X_ScalarWebApi_DeviceInfo that defines namespace urn:schemas-sony-com:av. Unfortunately it results in an error: {System.NullReferenceException: Object reference not set to an instance of an object. I'm mainly interested in ServiceList element, but it doesn't work as well. Platform - Windows 10 mobile.
Any ideas?
<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
<friendlyName>ILCE-6000</friendlyName>
<manufacturer>Sony Corporation</manufacturer>
<manufacturerURL>http://www.sony.net/</manufacturerURL>
<modelDescription>SonyDigitalMediaServer</modelDescription>
<modelName>SonyImagingDevice</modelName>
<UDN>uuid:000000001000-1010-8000-62F1894EE7BE</UDN>
<serviceList>
<service>
<serviceType>urn:schemas-sony-com:service:ScalarWebAPI:1</serviceType>
<serviceId>urn:schemas-sony-com:serviceId:ScalarWebAPI</serviceId>
<SCPDURL/>
<controlURL/>
<eventSubURL/>
</service>
</serviceList>
<av:X_ScalarWebAPI_DeviceInfo xmlns:av="urn:schemas-sony-com:av">
<av:X_ScalarWebAPI_Version>1.0</av:X_ScalarWebAPI_Version>
<av:X_ScalarWebAPI_ServiceList>
<av:X_ScalarWebAPI_Service>
<av:X_ScalarWebAPI_ServiceType>guide</av:X_ScalarWebAPI_ServiceType>
<av:X_ScalarWebAPI_ActionList_URL>http://192.168.122.1:8080/sony</av:X_ScalarWebAPI_ActionList_URL>
<av:X_ScalarWebAPI_AccessType/>
</av:X_ScalarWebAPI_Service>
<av:X_ScalarWebAPI_Service>
<av:X_ScalarWebAPI_ServiceType>accessControl</av:X_ScalarWebAPI_ServiceType>
<av:X_ScalarWebAPI_ActionList_URL>http://192.168.122.1:8080/sony</av:X_ScalarWebAPI_ActionList_URL>
<av:X_ScalarWebAPI_AccessType/>
</av:X_ScalarWebAPI_Service>
<av:X_ScalarWebAPI_Service>
<av:X_ScalarWebAPI_ServiceType>camera</av:X_ScalarWebAPI_ServiceType>
<av:X_ScalarWebAPI_ActionList_URL>http://192.168.122.1:8080/sony</av:X_ScalarWebAPI_ActionList_URL>
<av:X_ScalarWebAPI_AccessType/>
</av:X_ScalarWebAPI_Service>
</av:X_ScalarWebAPI_ServiceList>
</av:X_ScalarWebAPI_DeviceInfo>
</device>
</root>
Ah, the code:
XDocument xDoc = XDocument.Parse(xml_text);
//var av = xDoc.Root.GetDefaultNamespace();//.Attribute("xmlns");//
XNamespace av = "urn:schemas-sony-com:av";
System.Diagnostics.Debug.WriteLine(av);
<XElement> api_list = (List<XElement>)xDoc.Element(av + "X_ScalarWebAPI_DeviceInfo").Elements();
==EDIT==
Well, both solutions were ok, so I'm upvoting one and marking as answer the other :P
It was mentioned that the solution using only a 'local name' might cause false positive search results, so to be safe I'm using the first one. Thanks for help!
Element only returns elements directly beneath the current node. You need the to specify the entire path (note there are multiple namespaces):
XNamespace ns = "urn:schemas-upnp-org:device-1-0";
XNamespace av = "urn:schemas-sony-com:av";
var api_list = xDoc.Root.Element(ns + "device")
.Element(av + "X_ScalarWebAPI_DeviceInfo").Elements();
Using Xml Linq
XDocument doc = XDocument.Load(FILENAME);
XElement x_ScalarWebAPI_DeviceInfo = doc.Descendants().Where(x => x.Name.LocalName == "X_ScalarWebAPI_DeviceInfo").FirstOrDefault();
XNamespace ns = x_ScalarWebAPI_DeviceInfo.Name.Namespace;
The issue is indeed the Element or Elements methods only search the direct child elements. You can use Decendants() but you will get a collection, so you have to do First(expression) to het a single one.
But in the expression you can use .Name.LocalName to skip the whole namespace thing and look for just the name of the element.
For this question:
XDocument xDoc = XDocument.Parse(xml_text);
XElement x_ScalarWebAPI_DeviceInfo = doc.Descendants().First(x.Name.LocalName == "X_ScalarWebAPI_DeviceInfo");

Getting decendant node that contains ":"?

I have loaded a XML into a XDocument that looks something like this :
<MyXML xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MyApp.Client.Main.GUI.Report">
<Wrapper xmlns:d2p1="http://schemas.datacontract.org/2004/07/MyApp.Business.Entity">
<d2p1:_MultipelAttributId>156</d2p1:_MultipelAttributId>
</Wrapper>
</MyXML>
Now I need to get the value of _MultipelAttributId but when stating this :
XElement element = (from cml2 in doc.Descendants("d2p1:_MultipelAttributId") select cml2).FirstOrDefault();
I get exception about the ":"?
So how do I get the valiue?
XNamespace ns = "http://schemas.datacontract.org/2004/07/MyApp.Business.Entity";
var element = xDoc.Descendants(ns + "_MultipelAttributId").FirstOrDefault();
The element name you're querying is not correct. Try this:
XElement element = (
from cml2 in doc.Descendants()
where cml2.Name.LocalName == "_MultipelAttributId"
select cml2).FirstOrDefault();
This MSDN article explains well how to work with namespaces in Linq to XML
http://msdn.microsoft.com/en-us/library/bb669152.aspx
XNamespace d2p1 = "http://schemas.datacontract.org/2004/07/MyApp.Business.Entity";
XElement multipelAttributId = doc.Descendants(d2p1 + "_MultipelAttributId").FirstOrDefault();
Notice that you can seamly create an XNamespace or an XName from a string because Microsoft have writen inplicit casts for these types.

Reading Elements within a Namespace

I have an XML file that looks like:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="https://www.someurl.com/somefile.xslt"?>
<AutoInsuranceClaim xmlns="http://www.someurl.com/schemas/AutoInsuranceClaim">
<Identification>
<BaseOwner>3</BaseOwner>
<BaseType>ABC123</BaseType>
<BaseTypeRef>471038341757</BaseTypeRef>
</Identification>
</AutoInsuranceClaim>
and I'm trying to read the Identification node. Here's my code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"..\..\Data.xml");
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("ns", "http://www.someurl.com/schemas/AutoInsuranceClaim");
XmlNodeList nodeList = xmlDoc.SelectNodes(#"/ns:AutoInsuranceClaim/Identification", nsmgr);
Console.WriteLine("There are {0} nodes...", nodeList.Count);
I know I should get a least 1 value. My understanding of .NET XML parsing is that if you have a default namespace with no prefix, you have to create your own namespace. But this should have returned 1.
If not, what am I missing?
I might be grasping at straws here, but shouldn't you be namespacing both entities in your xpath expression?
XmlNodeList nodeList = xmlDoc.SelectNodes(#"/ns:AutoInsuranceClaim/ns:Identification", nsmgr);
XElement root = XElement.Load("Data.xml");
var identifications = root.Descendants()
.Where(x => x.Name.LocalName == "Identification")
.ToList()
The problem is that you're trying to find an Identification node without a namespace, but it will have defaulted to the same namespace as the parent due to the xmlns=... part. Try this:
var nodeList = xmlDoc.SelectNodes("/ns:AutoInsuranceClaim/ns:Identification",
nsmgr);
Having tried it myself, it printed a count of 1.
Personally I'd use LINQ to XML instead though, which makes namespace easier handling:
XDocument doc = XDocument.Load(#"..\..\Data.xml");
XNamespace ns = "http://www.someurl.com/schemas/AutoInsuranceClaim";
var nodes = doc.Root.Elements(ns + "Identification");

XML Document Parsing C#

I have a REST web service that creates a XMLDocument I am a bit confused on how access the inner text in FormattedPrice using XMLNode. I can grad offers but that will give me all the inner text.
<Offers>
<Offer>
<OfferListing>
<Price>
<Amount>1067</Amount>
<CurrencyCode>USD</CurrencyCode>
<FormattedPrice>$10.67</FormattedPrice>
</Price>
</OfferListing>
</Offer>
</Offers>
A quick walk-through of XPath will help immensely if you're using the Xml DOM.
This should meet your immediate need:
XmlNode n = doc.DocumentElement.SelectSingleNode("Offer/OfferListing/Price/FormattedPrice");
That will get you the first Offer's Formatted price (and assumes that your Offers node is the root). Other mechanisms exist in XPath that are a bit less brittle and that's where a tutorial would help you.
You are probably be best off using XPath.
XmlDocument doc = ...;
XmlNode fPrice;
XmlElement root = doc.DocumentElement;
fPrice= root.SelectSingleNode("/Price/FormattedPrice");
return fPrice.InnerText;
Here's a good example: http://www.codeproject.com/KB/cpp/myXPath.aspx
Use XElement to parse it:
string tmp = #"
<Offers>
<Offer>
<Price>
<Amount>1067</Amount>
<CurrencyCode>USD</CurrencyCode>
<FormattedPrice>$10.67</FormattedPrice>
</Price>
</Offer>
</Offers>";
XElement xml = XElement.Parse(tmp);
string formatedPrice = (string)xml.XPathSelectElement("/Offer/Price/FormattedPrice");
This should grab the $ amount:
var price = doc.SelectSingleNode(#"//Offer/Price/FormattedPrice");
string priceText = price.InnerText;
Load what you want into a XmlNodeList and then pull one explicitly or loop through them...
XmlNodeList pricesList = xmlDoc.GetElementsByTagName("FormattedPrice");
string firstPrice = pricesList[0].InnerText;
First off, your XML is invalid....you are missing the starting OfferListing tag.
Here is yet another option to grab the node text.
var xmlString = "<Offers><Offer><OfferListing><Price><Amount>1067</Amount<CurrencyCode>USD</CurrencyCode><FormattedPrice>$10.67</FormattedPrice></Price></OfferListing></Offer></Offers>";
var xDoc = new XmlDocument();
xDoc.LoadXml(xmlString);
var formattedPrice = xDoc.GetElementsByTagName("FormattedPrice")[0].InnerText;

Read attribute from xml

Can someone help me read attribute ows_AZPersonnummer with asp.net using c# from this xml structure
<listitems
xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:rs="urn:schemas-microsoft-com:rowset"
xmlns:z="#RowsetSchema"
xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<rs:data ItemCount="1">
<z:row
ows_AZNamnUppdragsansvarig="Peter"
ows_AZTypAvUtbetalning="Arvode till privatperson"
ows_AZPersonnummer="196202081276"
ows_AZPlusgiro="5456436534"
ows_MetaInfo="1;#"
ows__ModerationStatus="0"
ows__Level="1" ows_ID="1"
ows_owshiddenversion="6"
ows_UniqueId="1;#{11E4AD4C-7931-46D8-80BB-7E482C605990}"
ows_FSObjType="1;#0"
ows_Created="2009-04-15T08:29:32Z"
ows_FileRef="1;#uppdragsavtal/Lists/Uppdragsavtal/1_.000"
/>
</rs:data>
</listitems>
And get value 196202081276.
Open this up in an XmlDocument object, then use the SelectNode function with the following XPath:
//*[local-name() = 'row']/#ows_AZPersonnummer
Basically, this looks for every element named "row", regardless of depth and namespace, and returns the ows_AZPersonnummer attribute of it. Should help avoid any namespace issues you might be having.
The XmlNamespaceManager is your friend:
string xml = "..."; //your xml here
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("z", "#RowsetSchema");
XmlNode n = doc.DocumentElement
.SelectSingleNode("//#ows_AZPersonnummer", nsm);
Console.WriteLine(n.Value);
You can also use LINQ to XML:
XDocument xd = XDocument.Parse(xml);
XNamespace xns = "#RowsetSchema";
string result1 = xd.Descendants(xns + "row")
.First()
.Attribute("ows_AZPersonnummer")
.Value;
// Or...
string result2 =
(from el in xd.Descendants(xns + "row")
select el).First().Attribute("ows_AZPersonnummer").Value;
I'd say you need an XML parser, which I believe are common. This looks like a simple XML structure, so the handling code shouldn't be too hard.
Use <%# Eval("path to attribute") %> but you need to load the xml has a DataSource.
Otherwise you can load it using XmlTextReader. Here's an example.

Categories