Use where condition for retrieving the xml data with C# - c#

I want to fetch the data from an xml file. I am fetching the id of node from the previous page. And on next page I want to display the data from xml of that id. I am passing id of node using query string but when I run my code its give me this error
System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
Here is my code
XElement xid = XElement.Parse(Request.QueryString["ID"]);
// var id = Request.QueryString["ID"];
var doc = XDocument.Load(Server.MapPath("~/Data/BlogContent.xml"));
var result = doc.Descendants("post")
.Where(x => x.Element("id") == xid)
.Select(x => new
{
id = x.Element("id").Value,
title = x.Element("title").Value,
Discription = x.Element("Discription").Value,
dt = x.Element("dt").Value,
mnt = x.Element("mnt").Value,
yr = x.Element("yr").Value
}).OrderByDescending(x => x.id).Take(5);
Repeater1.DataSource = result;
Repeater1.DataBind();
Here is my xml
<?xml version="1.0" encoding="utf-8"?>
<content>
<post>
<id>1</id>
<title>fds</title>
<Discription>fdsafsdf</Discription>
<dt>21</dt>
<mnt>6</mnt>
<yr>2013</yr>
</post>
</content>
Please tell me where I am going wrong

You don't indicate the line causing the error, but I bet it's this one:
XElement xid = XElement.Parse(Request.QueryString["ID"]);
Most likely "ID" in your query string is an identifier of some sort, not XML - hence the error.
Something like this is what you want:
string xid = Request.QueryString["ID"];
Then you can use it in your where clause.
Also, there's an error in your where clause - you're trying to compare an XElement to a value - you need to get the value of the XElement using it's Value property:
Where(x => x.Element("id").Value == xid)
XElement.Value returns a string - so simply take the string value from the query string and use it in the comparison in your where clause.
Everything Put Together
string xid = Request.QueryString["ID"];
var doc = XDocument.Load(Server.MapPath("~/Data/BlogContent.xml"));
var result = doc.Descendants("post")
.Where(x => x.Element("id").Value == xid)
.Select(x => new
{
id = x.Element("id").Value,
title = x.Element("title").Value,
Discription = x.Element("Discription").Value,
dt = x.Element("dt").Value,
mnt = x.Element("mnt").Value,
yr = x.Element("yr").Value
}).OrderByDescending(x => x.id).Take(5);
Repeater1.DataSource = result;
Repeater1.DataBind();

Related

Way to populate class object in single LINQ XML query?

Given the following XML snippet, is there a way to both query and populate a class object in one LINQ statement? It's confusing because of the need to select using attribute values.
<data>
<array>
<item key="0">
<map>
<item key="mrid">53030</item>
<item key="mrtitle">GeneralFeedback</item>
</map>
</item>
</array>
</data>
Class:
public class Incident
{
public int ID { get; set; }
public string Title { get; set; }
}
Current (working) code (where result is the XML snippet as a string):
var data = XDocument.Parse(result);
var id = from item in data.Descendants("item")
where item.Attribute("key").Value == "mrid"
select item.Value;
var title = from item in data.Descendants("item")
where item.Attribute("key").Value == "mrtitle"
select item.Value;
var incident = new Incident
{
ID = Convert.ToInt32(id.FirstOrDefault()),
Title = title.FirstOrDefault()
};
Based on the answers given I learned some useful things and came up with this variation:
var incidents = data.Descendants("map")
.Select(i => i.Descendants("item")
.ToDictionary(m => m.Attribute("key").Value, m => m.Value))
.Where(i => i.ContainsKey("mrid")
&& i.ContainsKey("mrtitle"))
.Select(i => new Incident
{
ID = int.Parse(i["mrid"]),
Title = i["mrtitle"]
});
One thing I really like is that this creates an IEnumerable that allows for multiple incidents being present in the XML data.
is there a way to both query and populate a class object in one LINQ statement?
Yes, well sorta ... and it remains quite ugly. The below "single" multi-step LINQ statement ensures only the items that belong to the same map element get selected. Like your code sample, it will blow up in your face if the items with the required key values are missing (or the "mrid" element is not an int).
var key_vals = new List<string> { "mrid", "mrtitle" };
var xdoc = XDocument.Load(#"c:\temp\test.xml");
var incidents = xdoc.Descendants("map").Select(map => {
var items = map.Descendants("item").Where(i => key_vals.Contains(i.Attribute("key").Value));
var idItem = items.Where(x => x.Attribute("key").Value == "mrid").First();
var titleItem = items.Where(x => x.Attribute("key").Value == "mrtitle").First();
return new Incident {
ID = int.Parse(idItem.Value),
Title = titleItem.Value
};
});
foreach (var i in incidents)
Console.WriteLine("ID = {0}, Title = {1}", i.ID, i.Title);
It will produce the output below for your given xml input file:
ID = 53030, Title = GeneralFeedback
Check out this post to learn how to convert your XML schema to a C# class
Generate C# class from XML
Then you can use your new type and de-serialize your XML to a class
XmlSerializer serializer = new XmlSerializer(typeof(Incident));
using (StringReader reader = new StringReader(xmlDocumentText))
{
Incident incident= (Incident)(serializer.Deserialize(reader));
}
Alex has already given a perfect answer, but I find this a little more readable (:
The Where clause ensures each item found, has the keys required to construct an Incident.
var incidents = xdoc.Root
.Element("array")
.Elements("item")
.Select(i => i.Element("map")
.Elements("item")
.ToDictionary(m => m.Attribute("key").Value,
m => m.Value))
.Where(i => i.ContainsKey("mrid")
&& i.ContainsKey("mrtitle"))
.Select(i => new Incident
{
ID = int.Parse(i["mrid"]),
Title = i["mrtitle"]
});

XML to Linq C#, no results

I'm trying to grab the Filename from the below XML code where the partNum passed in by the end user matches the partNum of a JES.
My current code yields 0 results.
I will also be trying to grab the disrete attribute as well.
<JESs>
<JES partNum="116102440002" discrete="true">
<Filename>116-10244-0002_ILLK Collimator Cover Assy_Rev 3.docx</Filename>
</JES>
<JES partNum="116102440003" discrete="false">
<Filename>ILLK Collimator in Gimbal_Rev 4.docx</Filename>
</JES>
<JES partNum="116102440004" discrete="true">
<Filename>116-10244-0004_Collimator Cover Installation_Rev 1.docx</Filename>
</JES>
<JES partNum="116102440005" discrete="true">
<Filename>116-10244-0005_Collimator Lens Assembly_Rev 2.docx</Filename>
</JES>
</JESs>
C# Code
var FileName = (from n in xml.Descendants("JESs") where n.Element("JES").Attribute("partNum").Value == Convert.ToString(partNum) select n.Elements().Descendants().Elements()).ToList();
You can simplify (and fix) your query like this:
var partNumber = Convert.ToString(partNum);
var result = xml.Descendants("JES")
.FirstOrDefault(x => (string)x.Attribute("partNum") == partNumber);
if(result != null)
{
var fileName = (string)result.Element("Filename");
}
Shouldn't it be:
var FileNames = (from n in xml.Descendants("JESs")
where n.Element("JES").Attribute("partNum").Value == Convert.ToString(partNum)
select n.Element("JES").Element("Filename")).ToList();
Try this
var fileName = (from x in xml.Elements()
where x.Attribute("partNum").Value == "xxxx"
select x.Element("Filename").Value).FirstOrDefault();
//replace xxxx with actual value
If no elements found for given partNum, fileName will be null

How to use "let" to deal with missing XML nodes?

I'm updating my question here based on some feedback I got in response to my original post. I'm trying to parse some xml in an XDocument and not getting the results that I expect. Based on feedback below I've added the two let statements below, but they're not working as expected and I assume that I've got them wrong somehow. The problem is that when the CoverArt and Biography are not present in the XML result I get nothing back from my query Here is the code that I am running:
public List<Album> ParseResults(XDocument inputDoc)
{
var albums = new List<Album>();
try
{
albums = (from item in inputDoc.Descendants("ALBUM")
select new Album
{
let CoverArt = item.Elements("URL").First(u => u.Attribute("TYPE").Value == "COVERART") ?? new XElement("COVERART")
let Biography = item.Elements("URL").First(u => u.Attribute("TYPE").Value == "ARTIST_BIOGRAPHY") ?? new XElement("ARTIST_BIOGRAPHY")
AlbumId = (string)item.Element("GN_ID"),
ArtistName = (string)item.Element("ARTIST"),
AlbumName = (string)item.Element("TITLE"),
TrackCount = (int)item.Element("TRACK_COUNT"),
Year = (string)item.Element("DATE"),
Genre = (string)item.Element("GENRE"),
CoverArt = item.Elements("URL").First(u => u.Attribute("TYPE").Value == "COVERART").Value.ToString(),
Biography = item.Elements("URL").First(u => u.Attribute("TYPE").Value == "ARTIST_BIOGRAPHY").Value.ToString(),
Tracks = item.Elements("TRACK")
.Select(t => new Track
{
AlbumId = (string)item.Element("GN_ID"),
TrackNumber = (int)t.Element("TRACK_NUM"),
TrackName = (string)t.Element("TITLE"),
TrackId = (string)t.Element("GN_ID"),
}).ToList()
}).ToList();
}
catch (Exception e)
{
}
return albums;
}
Here is the XML, I'm running it against. In this example there is no Biography in the XML that I'm querying:
<RESPONSE STATUS="OK">
<ALBUM>
<GN_ID>63074689-EDADA0FEDE93683CA03C6D38520A4D88</GN_ID>
<ARTIST>Green Day</ARTIST>
<TITLE>American Idiot</TITLE>
<PKG_LANG>ENG</PKG_LANG>
<DATE>2004</DATE>
<GENRE NUM="105222" ID="35474">Punk</GENRE>
<TRACK_COUNT>13</TRACK_COUNT>
<TRACK>
<TRACK_NUM>1</TRACK_NUM>
<GN_ID>63074690-456E41C113DC8354DC6B25421F2C7989</GN_ID>
<TITLE>American Idiot</TITLE>
</TRACK>
<TRACK>
<TRACK_NUM>2</TRACK_NUM>
<GN_ID>63074691-70EFB1E8EB31B5296D5822E55343EFA9</GN_ID>
<TITLE>Jesus Of Suburbia / City Of The Damned / I Don't Care / Dearly Beloved / Tales Of Another Broken Home</TITLE>
</TRACK>
<URL TYPE="COVERART" SIZE="THUMBNAIL" WIDTH="75" HEIGHT="75">http://akamai-b.cdn.cddbp.net/cds/2.0/cover/0A1A/BABF/DEBC/CF21_thumbnail_front.jpg</URL>
</ALBUM>
</RESPONSE>
</RESPONSES>
Can anyone help me out with this?
The problem is your XML has no URL elements, so the query is failing and throws an InvalidOperationException. Update the 2 URL lines in your query to the following:
CoverArt = (string)item.Elements("URL").FirstOrDefault(u => u.Attribute("TYPE").Value == "COVERART"),
Biography = (string)item.Elements("URL").FirstOrDefault(u => u.Attribute("TYPE").Value == "ARTIST_BIOGRAPHY"),
The query uses FirstOrDefault and will return null if the result isn't found, then casts it to a string. Alternately, you could use a let clause earlier in the query and assign it to item.Elements("URL") then do a ternary check before using it in the query, or return null. The result is the same, but the style chosen depends on what you want to do if it's null and gives you some more flexibility.

XML query - retrieving values

To my great frustration, I spent half a day to build some queries for an XML document and I don't know anything more than I knew before I started. So, I'm taking the easy way out and ask again for help.
The XML code is like this:
<?xml version="1.0" ?>
<Main>
<alpha Id = "AlphaId_1">
<beta>AlphaId1_Beta</beta>
<gamma>AlphaId1_Gama</gamma>
<delta Type = "A">AlphaId1_DeltaTypeA</delta>
<delta Type = "B">AlphaId1_DeltaTypeB</delta>
<kapa Id="01">
<description>AlphaId1_KapaId1_Descr</description>
<name>AlphaId1KapaId1_Name</name>
<teta>AlphaId1KapaId1_Teta</teta>
</kapa>
<kapa Id="02">
<description>AlphaId1KapaId2_Descr</description>
<name>AlphaId1KapaId2_Name</name>
<teta>AlhaId1KapaId2_Teta</teta>
</kapa>
</alpha>
<alpha Id = "AlphaId_2">
<beta>AlphaId2_Beta</beta>
<gamma>AlphaId2_Gama</gamma>
<delta Type = "A">AlphaId2_DeltaTypeA</delta>
<delta Type = "B">AlphaId2_DeltaTypeB</delta>
<kapa Id="01">
<description>AlphaId2_KapaId1_Descr</description>
<name>AlphaId2KapaId2_Name</name>
<teta>AlphaId2KapaId2_Teta</teta>
</kapa>
<kapa Id="02">
<description>AlphaId1KapaId2_Descr</description>
<name>AlphaId2KapaId2_Name</name>
<teta>AlhaId2KapaId2_Teta</teta>
</kapa>
</alpha>
</Main>
I am looking for a query to retrieve for example the value "AlphaId2_DeltaTypeA".
The second query should retrieve all the description values from every KapaId for a selected AlphaId.
The only code I could come up with is
XDocument xdoc = XDocument.Load(#"doc.xml");
IEnumerable<XElement> list1 = xdoc.Root.Descendants("delta");
var cifmi =
from el in list1
where (string)el.Attribute("Type") == "A"
select el;
foreach (XElement el in cifmi)
{
textBox1.AppendText(el.Value + System.Environment.NewLine);
}
The code finds two values instead of one.
var xDoc = XDocument.Load("Input.txt");
var alpha = (from a in xDoc.Root.Elements("alpha")
let deltas = a.Elements("delta")
let deltaA = deltas.First(x => (string)x.Attribute("Type") == "A")
where (string)deltaA == "AlphaId2_DeltaTypeA"
select a).First();
var descriptions = alpha.Elements("kapa")
.Select(x => (string)x.Element("description")).ToList();
It will only look for <delta> which has both Type="A" and value of AlphaId2_DeltaTypeA. If you only care about value try that one:
var alpha = (from a in xDoc.Root.Elements("alpha")
let deltas = a.Elements("delta")
where deltas.Any(x => (string)x == "AlphaId2_DeltaTypeA")
select a).First();
Update
*alpha Id == "AlphaId_1" and delta Type = "A" and the answer is AlphaId1_DeltaTypeA*
var alpha = (string)xDoc.Root
.Elements("alpha")
.First(x => (string)x.Attribute("Id") == "AlphaId_1")
.Elements("delta")
.First(x => (string)x.Attribute("Type") == "A");

Trying to read XML attributes via LINQ

xDoc variable loads the XML content but I am not able to retrieve any information. It comes back NULL:
var xDoc = XDocument.Load(Config.CredentialFileName);
//method 1
IEnumerable<XElement> rows = from row in xDoc.Descendants("domain")
where (string)row.Attribute("name") == "TEST"
select row;
//method 2
var list = xDoc.Descendants("domain")
.Select(d => new
{
name = d.Attribute("name").Value,
username = d.Attribute("username").Value,
password = d.Attribute("password").Value //,
})
.Where(a => a.name == "TEST")
.ToList();
XML file:
<domains>
<domain name="TEST" userName="test" password="tSEvmlsmwEkjSxUwrCVf3G6"/>
</domains>
Thank you
Your first method works just fine with xml you provided. Make sure you are parsing xml with exactly same structure. Also check that you have at least one domain element with name equal to TEST. And make sure you don't have namespaces defined in your xml.
Second method has typo in userName attribute name (you have lower case username):
var list = xDoc.Descendants("domain")
.Select(d => new {
name = d.Attribute("name").Value,
username = d.Attribute("userName").Value, // <-- typo here
password = d.Attribute("password").Value
})
.Where(a => a.name == "TEST")
.ToList();
Also, I'd recommend to use casting instead of reading node Value property, because getting this property will throw an exception if node not exist.
var domains = from d in xDoc.Descendants("domain")
let name = (string)d.Attribute("name")
where name == "TEST"
select new {
Name = name,
Username = (string)d.Attribute("userName"),
Password = (string)d.Attribute("password")
};
See below.
var xDoc= XElement.Load(Config.CredentialFileName);
var result = xDoc.Elements("domain").Where(x => x.Attribute("name").Value.Equals("TEST")).ToList();

Categories

Resources