Traversing XML document using LINQ - c#

I'm trying to read in the address position values of a specific product (e.g. Payslips) in the below XML
<INI>
<ReportTemplate>report_template_land.pdf</ReportTemplate>
<ReportAccountID>Reports</ReportAccountID>
<!--Table for sending the documents to different channels-->
<ChannelDeliveryTable>ChannelDeliveryTable.csv</ChannelDeliveryTable>
<Documents>
<Payslip>
<Address>
<distanceInPixelsFromLeft>76</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Payslip>
<Invoice>
<Address>
<distanceInPixelsFromLeft>65</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Invoice>
</Documents>
</INI>
I had couple of attempts which all failed. The below code shows my last attempt. Could you please help. Thanks in advance.
float distanceInPixelsFromLeftAddr;
float distanceInPixelsFromBottomAddr;
float widthAddr;
float heightAddr;
try
{
//var addrPos = from xml in XmlDoc.Elements("Payslip").Descendants("Address")
var addrPos = from xml in XmlDoc.Descendants("Payslip").Descendants("Address")
select new
{
distanceInPixelsFromLeftAddr = xml.Element("distanceInPixelsFromLeft").Value,
distanceInPixelsFromBottomAddr = xml.Element("distanceInPixelsFromBottom").Value,
widthAddr = xml.Element("width").Value,
heightAddr = xml.Element("height").Value
};
foreach (var node in addrPos)
{
distanceInPixelsFromLeftAddr = float.Parse(node.distanceInPixelsFromLeftAddr);
distanceInPixelsFromBottomAddr = float.Parse(node.distanceInPixelsFromBottomAddr);
widthAddr = float.Parse(node.widthAddr);
heightAddr = float.Parse(node.heightAddr);
}
}

Given no default namespace is involved, the following query works just fine against the XML posted in question :
var addrPos = from xml in XmlDoc.Descendants("Payslip").Elements("Address")
select new
{
distanceInPixelsFromLeftAddr = (string)xml.Element("distanceInPixelsFromLeft"),
distanceInPixelsFromBottomAddr = (string)xml.Element("distanceInPixelsFromBottom"),
widthAddr = (float)xml.Element("width"),
heightAddr = (float)xml.Element("height")
};
Notice how you can cast XElement directly to the appropriate type such as string or float.
See the demo below or see it live in dotnetfiddle :
var raw = #"<INI>
<ReportTemplate>report_template_land.pdf</ReportTemplate>
<ReportAccountID>Reports</ReportAccountID>
<!--Table for sending the documents to different channels-->
<ChannelDeliveryTable>ChannelDeliveryTable.csv</ChannelDeliveryTable>
<Documents>
<Payslip>
<Address>
<distanceInPixelsFromLeft>76</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Payslip>
<Invoice>
<Address>
<distanceInPixelsFromLeft>65</distanceInPixelsFromLeft>
<distanceInPixelsFromBottom>580</distanceInPixelsFromBottom>
<width>255</width>
<height>125</height>
</Address>
</Invoice>
</Documents>
</INI>
";
var XmlDoc = XDocument.Parse(raw);
var addrPos = from xml in XmlDoc.Descendants("Payslip").Elements("Address")
select new
{
distanceInPixelsFromLeftAddr = (string)xml.Element("distanceInPixelsFromLeft"),
distanceInPixelsFromBottomAddr = (string)xml.Element("distanceInPixelsFromBottom"),
widthAddr = (float)xml.Element("width"),
heightAddr = (float)xml.Element("height")
};
foreach (var node in addrPos)
{
Console.WriteLine(node);
}
Output :
{ distanceInPixelsFromLeftAddr = 76, distanceInPixelsFromBottomAddr = 580, widthAddr = 255, heightAddr = 125 }

Related

XDocument just reads the first title?

I need to parse xml but my code just parses one title not all.
How can I parse part ?
This is my code:
CustomResponse itemCustom = new CustomResponse ();
XDocument response = XDocument.Parse(responseXml);
XElement rootElement = response.Root;
foreach (XElement sellResponse in rootElement.Elements())
{
itemCustom .ErrorCode = sellResponse.Element("ErrorCode").Value;
itemCustom .ErrorMessage = sellResponse.Element("ErrorMessage").Value;
itemCustom .CustomerID= sellResponse.Element("CustomerID").Value;
itemCustom .CustomerType= sellResponse.Element("CustomerType").Value;
}
This is my xml:
<?xml version="1.0" encoding="utf-8"?>
<TINS_XML_DATA>
<Header>
<ErrorCode>WAATS</ErrorCode>
<ErrorMessage>UTL</ErrorMessage>
</Header>
<Customer>
<CustomerID>UTL11111111111111111111</CustomerID>
<CustomerType>NSell</CustomerType>
</Customer>
</TINS_XML_DATA>
Try something like this:
foreach (XElement sellResponse in rootElement.Elements())
{
if (sellResponse.Name == "Header")
{
itemCustom.ErrorCode = sellResponse.Element("ErrorCode").Value;
itemCustom.ErrorMessage = sellResponse.Element("ErrorMessage").Value;
}
else if (sellResponse.Name == "Customer")
{
itemCustom.CustomerID = sellResponse.Element("CustomerID").Value;
itemCustom.CustomerType = sellResponse.Element("CustomerType").Value;
}
}
Update: You could also use XPath to find required elements as like below:
var xDoc = new XmlDocument();
xDoc.LoadXml(xml);
var errorMessage = xDoc.SelectNodes("//ErrorMessage")[0].InnerText;
This is my solved:
var xDoc = new XmlDocument();
xDoc.LoadXml(responseXml);
itemSell.ErrorCode = xDoc.SelectNodes("//ErrorCode")[0].InnerText;
itemSell.ErrorMessage = xDoc.SelectNodes("//ErrorMessage")[0].InnerText;
itemSell.CustomerID= xDoc.SelectNodes("//CustomerID")[0].InnerText;

Why is XPathNodeIterator not finding desired path?

I am having a problem with XPathNodeIterator grabbing the data from the given path. When debugging, pNav has all the values from the xml file. However iterator shows a count of 0. It never enters the while loop. Any help would be appreciated.
C#
XPathDocument pdoc = new XPathDocument("Courses.xml");
XPathNavigator pNav = pdoc.CreateNavigator();
XPathNodeIterator iterator = pNav.Select("/Courses/Course");
while (iterator.MoveNext())
{
XPathNodeIterator it = iterator.Current.Select("Name");
it.MoveNext();
string courseName = it.Current.Value;
it = iterator.Current.Select("Code");
it.MoveNext();
string courseCode = it.Current.Value;
Console.WriteLine("{0} {1}", courseName, courseCode);
}
XML:
<Courses xmlns="http://xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="Courses.xsd">
<Course>
<Code Undergrad="240"/>
<Name>Biology</Name>
<Instructor>
<Name>
<First>John</First>
<Last>Doe</Last>
</Name>
<Contact>
<Phone>898-989-8989</Phone>
</Contact>
</Instructor>
<Room>515</Room>
</Course>
</Courses>
I expect the output to be
Name = Biology, Code = 240
Because you have
xmlns="http://xml"
in your XML file you need to add a XmlNamespaceManager to allow the navigator to find the nodes. If you remove the xmlns="http://xml" from your XML then you won't need to use an XmlNamespaceManager.
Also the Select method returns a collection of nodes - you need to call SelectSingleNode to get the node you want. E.G.
XPathDocument pdoc = new XPathDocument("Courses.xml");
XPathNavigator pNav = pdoc.CreateNavigator();
var manager = new XmlNamespaceManager(pNav.NameTable);
manager.AddNamespace("cs", "http://xml");
XPathNodeIterator iterator = pNav.Select("/cs:Courses/cs:Course", manager);
while(iterator.MoveNext())
{
var nameNode = iterator.Current.SelectSingleNode("cs:Name", manager);
string courseName = nameNode.Value;
var codeNode = iterator.Current.SelectSingleNode("cs:Code", manager);
codeNode.MoveToFirstAttribute();
string courseCode = codeNode.Value;
Console.WriteLine("{0} {1}", courseName, courseCode);
}
When you get to the Code element, you need to move to the first attribute to get the value, otherwise Value property will return an empty string
It needs to pass namespace resolver to Select-method:
const string xml = #"
<Courses xmlns=""http://xml"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:schemaLocation=""Courses.xsd"">
<Course>
<Code Undergrad=""240""/>
<Name>Biology</Name>
<Instructor>
<Name>
<First>John</First>
<Last>Doe</Last>
</Name>
<Contact>
<Phone>898-989-8989</Phone>
</Contact>
</Instructor>
<Room>515</Room>
</Course>
<Course>
<Code Undergrad=""000""/>
<Name>Math</Name>
<Instructor>
<Name>
<First>John</First>
<Last>Doe</Last>
</Name>
<Contact>
<Phone>898-989-8989</Phone>
</Contact>
</Instructor>
<Room>515</Room>
</Course>
</Courses>";
using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(xml)))
{
var pdoc = new XPathDocument(stream);
var pNav = pdoc.CreateNavigator();
var manager = new XmlNamespaceManager(pNav.NameTable);
manager.AddNamespace("cs", "http://xml");
var iterator = pNav.Select("/cs:Courses/cs:Course", manager);
foreach (XPathNavigator node in iterator)
{
var courseName = node.SelectSingleNode("cs:Name", manager)?.Value;
var courseCode = node.SelectSingleNode("cs:Code", manager)?.GetAttribute("Undergrad", string.Empty);
Console.WriteLine("{0} {1}", courseName, courseCode);
}
}

Using Linq to get XML data from database

I'm struggling to get the values of a single xml node using Linq.
Here is my XML.
<?xml version="1.0" encoding="utf-8"?>
<record>
<AddressLine1>abcd street</AddressLine1>
<AddressLine2>xyz AVE</AddressLine2>
<AddressCity>Illinois</AddressCity>
<AddressState>Chicago</AddressState>
<AddressZip>23434</AddressZip>
</record>
And here is my c# code
XElement xmlDoc = XElement.Parse(varQ.Content);
//When I debug I find that xmlDoc contains the XML. So that is alright.
var q = (from lpi in xmlDoc.Descendants("record")
select new { AddressLine1 = lpi.Element("AddressLine1").Value,
AddressLine2 = lpi.Element("AddressLine2").Value,
AddressCity = lpi.Element("AddressCity").Value,
AddressCountry = lpi.Element("AddressCountry").Value,
AddressState = lpi.Element("AddressState").Value,
AddressZip = lpi.Element("AddressZip").Value,
}).FirstOrDefault();
var q shows null. Can u please help me find out what is wrong here?
Replace Descendants on DescendantsAndSelf:
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<record>
<AddressLine1>abcd street</AddressLine1>
<AddressLine2>xyz AVE</AddressLine2>
<AddressCity>Illinois</AddressCity>
<AddressState>Chicago</AddressState>
<AddressZip>23434</AddressZip>
</record>";
XElement xmlDoc = XElement.Parse(xml);
var q = (from lpi in xmlDoc.DescendantsAndSelf("record")
select new
{
AddressLine1 = (string)lpi.Element("AddressLine1"),
AddressLine2 = (string)lpi.Element("AddressLine2"),
AddressCity = (string)lpi.Element("AddressCity"),
AddressCountry = (string)lpi.Element("AddressCountry"),
AddressState = (string)lpi.Element("AddressState"),
AddressZip = (string)lpi.Element("AddressZip"),
}).FirstOrDefault();
Console.WriteLine(q);
Print:
{ AddressLine1 = abcd street, AddressLine2 = xyz AVE, AddressCity = Illinois, AddressCountry = , AddressState = Chicago, AddressZip = 23434 }
Link: https://dotnetfiddle.net/fXQivX

Attempting to get single value from XDocument, nothing appears to work

I am just trying to get the value from MessageInfo.. Sender here is an excerpt of the xml. I just want the "Senders" value. I have tried many different things with XDocument, and wanted to use a Linq Query
I have tried,
var query1 = doc.Descendants("MessageInfo").Select(s => new MessageInfo
{
SYSGENID = s.Element("SysGenID").Value,
TIME_STAMP = s.Element("TimeStamp").Value,
SENDER = s.Element("Sender").Value,
RECEIVER = s.Element("Receiver").Value,
ENTITY_CODE = s.Element("EntityCode").Value
}).FirstOrDefault();
the query1 returns null. Following is example of xml
I also have tried
XDocument doc = XDocument.Load(filePath);
var messageInfo = doc.Root.Elements("MessageInfo");
var res = from m in messageInfo
select new
{
msgInfo = m.Element("MessageInfo").Value
};
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<PutSchedule xmlns="http://www.nwpp.org/eide">
<MessageInfo>
<SysGenID>4431</SysGenID>
<TimeStamp>2014-08-12T10:34:28.068000</TimeStamp>
<Sender>611</Sender>
<Receiver>WECC</Receiver>
<EntityCode>611</EntityCode>
</MessageInfo>
<Schedules>
<Schedule>
<ScheduleDescription>
<StartTime>2014-08-12T00:00:00</StartTime>
<EndTime>2014-08-15T00:00:00</EndTime>
<AccountCode>259S.NRGREEN_G1.BaseMW</AccountCode>
</ScheduleDescription>
<Quantities>
You have a namespace that you have to use.
XNamespace ns = "http://www.nwpp.org/eide";
var query1 = doc.Descendants(ns +"MessageInfo").Select(s => new MessageInfo
{
SYSGENID = s.Element(ns +"SysGenID").Value,
TIME_STAMP = s.Element(ns +"TimeStamp").Value,
SENDER = s.Element(ns +"Sender").Value,
RECEIVER = s.Element(ns +"Receiver").Value,
ENTITY_CODE = s.Element(ns +"EntityCode").Value
}).FirstOrDefault();

XML parsing with LINQ, has 2 tags with the same name but different level and not always has a value

I have the following XML:
<request>
<book>
<id>1833801</id>
<title>The Yiddish Policemen's Union </title>
<work>
<id>1234</id>
<name/>
</work>
<similar_books>
<book><id>243859</id><title>Stations of Tide</title> <isbn>0380817616</isbn>
<authors><author><id>14454</id><name>Michael Swanwick</name></author> </authors>
</book>
</similar_books>
<authors>
<author>
<id>2715</id><name>Michael Chabon</name>
<ratings_count>215884</ratings_count></author>
</authors>
<popular_shelves>
<shelf name="jewish" count="104"/><shelf name="sci-fi" count="100"/>
</popular_shelves>
</book>
</request>
I want to have all tags with their respective values, and I am using following code:
HttpWebRequest oReq = (HttpWebRequest)WebRequest.Create(uriRoot);
HttpWebResponse resp = (HttpWebResponse)oReq.GetResponse();
log.Info(" (ISBN= " + isbn10 + ") Http request has response.");
if (resp.ContentType.StartsWith("application/xml", StringComparison.InvariantCultureIgnoreCase))
{
Stream resultStreamISBN = resp.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); //encoding for non-latin chars
StreamReader responseReader = new StreamReader(resultStreamISBN, encode);
XDocument xdoc = XDocument.Parse(responseReader.ReadToEnd());
var books = (from u in xdoc.Descendants().Elements("book")
select new
{
id = (string)u.Element("title"),
title = (string)u.Element("title"),
works = (from i in u.Elements("work")
select new
{
work_best_book_id = (int)i.Element("id"),
work_name = (string)i.Element("name"),
}).ToList(),
authors = (from i in u.Elements("authors").Elements("author")
select new
{
id = (int)i.Element("id"),
name = (string)i.Element("name"),
rating = (int)i.Element("rating_count")
}).ToList(),
popular_shelves = (from i in u.Elements("popular_shelves").Elements("shelf")
select new
{
name = (string)i.Attribute("name"),
count = (int)i.Attribute("count")
}).ToList(),
}).ToList();
The code returns null values and is not working properly. I also should note that different xml files may not have values for all the tags.
Any suggestions on how I can improve my code?
Check for missing elements like such:
name = i.Element("name") == null ? null : i.Element("name")
For value types, you make have to change them to nullable types for this to work correctly.
You can transform
id = (int)i.Element("id"),
to
id = (int?)i.Element("id"),
Whole modified code
// I am inserting the following line to demonstrate how I load the XML in test code
// Basically, I copied & pasted xml in a file OP gave named request.xml
//XDocument xdoc = XDocument.Load( #"d:\Data\request.xml");
EDIT
XDocument xdoc = XDocument.Load("http://www.goodreads.com/book/isbn?isbn=0007295685&key=lbScLXWyNGQ1q0BDoFFSg");
var books = (from u in xdoc.Descendants().Elements("book")
select new
{
id = (string)u.Element("title"),
title = (string)u.Element("title"),
works = (from i in u.Elements("work")
select new
{
work_best_book_id = (int?)i.Element("id"),
work_name = (string)i.Element("name"),
}).ToList(),
authors = (from i in u.Elements("authors").Elements("author")
select new
{
id = (int?)i.Element("id"),
name = (string)i.Element("name"),
rating = (int?)i.Element("rating_count")
}).ToList(),
popular_shelves = (from i in u.Elements("popular_shelves").Elements("shelf")
select new
{
name = (string)i.Attribute("name"),
count = (int?)i.Attribute("count")
}).ToList(),
}).ToList();

Categories

Resources