Linq to XML Order by descending? - c#

I am having trouble with the OrderByDescending. It does not sort it correctly.
I have an XML file that looks like this:
<player id="3">
<name>David, Backham</name>
<goals>155</goals>
</player>
I am trying to display 3 players with the highest amount of goals.
XDocument doc = XDocument.Load("players.xml");
/// .OrderByDescending(r => r.Attribute("goals"))
var players = from r in doc.Descendants("player").OrderByDescending(r => r.Value)
select new
{
Name = r.Element("name").Value + " ",
Goal = r.Element("goals").Value + " ",
};
foreach (var r in players)
{
Console.WriteLine(r.Name + r.Goal);
}

Perhaps like this:
var players =
(from r in doc.Descendants("player")
orderby int.Parse(r.Element("goals").Value) descending
select new
{
Name = r.Element("name").Value + " ",
Goal = r.Element("goals").Value + " ",
})
.Take(3);
Or in fluent syntax:
var players = doc.Descendants("player")
.OrderByDescending(r => int.Parse(r.Element("goals").Value))
.Select(r => new
{
Name = r.Element("name").Value + " ",
Goal = r.Element("goals").Value + " ",
})
.Take(3);
Note that you'll need to parse the string value to an integer to get the correct sort order.
To filter the results, you can just do this:
var players =
(from r in doc.Descendants("player")
where r.Element("name").Value.StartsWith("David")
orderby int.Parse(r.Element("goals").Value) descending
select new
{
Name = r.Element("name").Value + " ",
Goal = r.Element("goals").Value + " ",
})
.Take(3);
Or in fluent syntax:
var players = doc.Descendants("player")
.Where(r => r.Element("name").Value.StartsWith("David"))
.OrderByDescending(r => int.Parse(r.Element("goals").Value))
.Select(r => new
{
Name = r.Element("name").Value + " ",
Goal = r.Element("goals").Value + " ",
})
.Take(3);

Related

c# List inside list XML Linq

I have the following statement
xdoc.Descendants("Father").Select(p => new
{
Son1 = (string)p.Element("Son1").Value,
Son2 = (string)p.Element("Son2").Value,
Son3= (string)p.Element("Son3").Value,
Son4 = (string)p.Element("Son4").Value,
Son5 = (string)p.Element("Son5").Value
}).ToList().ForEach(p =>
{
Response.Write("Son1= " + p.Son1 + " ");
Response.Write("Son2=" + p.Son2 + " ");
Response.Write("Son3=" + p.Son3 + " ");
Response.Write(("Son4 =") + p.Son4 + " ");
Response.Write(("Son5 =") + p.Son5 + " ");
Response.Write("<br />");
});
and it works fine as long as i have only one instance of each son , the problem is that i have multiple instances of Son5, and i donĀ“t know how to put Son5 inside of a list
Here is my XML code Example:
If you have several elements of same type, then you should parse them to list or other collection:
var fathers = from f in xdoc.Descendants("Father")
select new {
Son1 = (string)f.Element("Son1"),
Son2 = (string)f.Element("Son2"),
Son3= (string)f.Element("Son3"),
Son4 = (string)f.Element("Son4"),
Son5 = f.Elements("Son5").Select(s5 => (string)s5).ToList()
};
Some notes:
Don't use .Value of XElement or XAttribute - you can cast element itself to appropriate data type without accessing its value. Benefits - less code, more reliable in case element is missing (you will not get NullReferenceException)
Consider to use int or int? as elemenent values if your elements contain integer values
If you have single Father element, then don't work with collection of fathers. Just get xml root and check whether it's null or not. After that you can create single father object.
Writing response
foreach(var father in fathers)
{
Response.Write($"Son1={father.Son1} ");
Response.Write($"Son2={father.Son2} ");
Response.Write($"Son3={father.Son3} ");
Response.Write($"Son4={father.Son4} ");
Response.Write(String.Join(" ", father.Son5.Select(son5 => $"Son5={son5}"));
Response.Write("<br />");
}
Try this:
xdoc.Descendants("Father").Select(p => new
{
Son1 = p.Element("Son1").Value,
Son2 = p.Element("Son2").Value,
Son3= p.Element("Son3").Value,
Son4 = p.Element("Son4").Value,
Sons5 = p.Elements("Son5").Select(element => element.Value).ToList()
}).ToList().ForEach(p =>
{
Response.Write("Son1= " + p.Son1 + " ");
Response.Write("Son2=" + p.Son2 + " ");
Response.Write("Son3=" + p.Son3 + " ");
Response.Write("Son4 =" + p.Son4 + " ");
p.Sons5.ForEach(son5 => Response.Write("Son5 =" + son5 + " "));
Response.Write("<br />");
});
That will create a list of Son5 within your list of items, which you can iterate in the ForEach with another ForEach.

Replacing a Char or part of a string to conform with an SQL query

I know I've not gone about this in the easiest of manners but i'm fairly new to C#.
I'm trying to make sure that when I select from only 1,2,3 or 4 of the 5 checked list boxes that i do not end up with the following
&& or &&& etc... I would like the final string to be xxxxx&xxxxx&xxx or similar as the String is added to a preset SQL query.
Code is below:
any help would be appreciated I've had a look around online for some help but not found much I was able to get my head round.
List<object> list = checkedListBox1.CheckedItems.OfType<object>().ToList();
List<object> list2 = checkedListBox2.CheckedItems.OfType<object>().ToList();
List<object> list3 = checkedListBox3.CheckedItems.OfType<object>().ToList();
List<object> list4 = checkedListBox4.CheckedItems.OfType<object>().ToList();
List<object> list5 = checkedListBox5.CheckedItems.OfType<object>().ToList();
string selected_fields_cb1 = string.Join(" ", list.ToArray());
string selected_fields_cb2 = string.Join(" ", list2.ToArray());
string selected_fields_cb3 = string.Join(" ", list3.ToArray());
string selected_fields_cb4 = string.Join(" ", list4.ToArray());
string selected_fields_cb5 = string.Join(" ", list5.ToArray());
string allSelected = (selected_fields_cb1 + " " + selected_fields_cb2 + " " + selected_fields_cb3 +
" " + selected_fields_cb4 + " " + selected_fields_cb5 + "");
string allSelected2 = allSelected.Replace( " ", "&" );
string allSelected3 = allSelected2.TrimEnd('&');
If I understand well, you try to add spaces and then replace these spaces by &.
It would be easier to do this directly.
List<object> list = checkedListBox1.CheckedItems.OfType<object>().ToList();
List<object> list2 = checkedListBox2.CheckedItems.OfType<object>().ToList();
List<object> list3 = checkedListBox3.CheckedItems.OfType<object>().ToList();
List<object> list4 = checkedListBox4.CheckedItems.OfType<object>().ToList();
List<object> list5 = checkedListBox5.CheckedItems.OfType<object>().ToList();
var allObjects = list.Concat(list2).Concat(list3).Concat(list4).Concat(list5);
var res = string.Join("&", allObjects);

Combining various xml elements into one collection using GML/C#/LINQ

I am attempting to read a GML file in C#. I would like to store all the returned data in one object. So far I have been able to return all the data but in 3 separate objects:
XDocument doc = XDocument.Load(fileName);
XNamespace gml = "http://www.opengis.net/gml";
// Points
var points = doc.Descendants(gml + "Point")
.Select(e => new
{
POSLIST = (string)e.Element(gml + "pos")
});
// LineString
var lineStrings = doc.Descendants(gml + "LineString")
.Select(e => new
{
POSLIST = (string)e.Element(gml + "posList")
});
// Polygon
var polygons = doc.Descendants(gml + "LinearRing")
.Select(e => new
{
POSLIST = (string)e.Element(gml + "posList")
});
I would like to instead of creating 3 separate objects, I would like to create one object as follows:
var all = doc.Descendants(gml + "Point")
doc.Descendants(gml + "LineString")
doc.Descendants(gml + "LinearRing")....
But need some help. Thanks before hand.
Sample Data:
<gml:Point>
<gml:pos>1 2 3</gml:pos>
</gml:Point>
<gml:LineString>
<gml:posList>1 2 3</gml:posList>
</gml:LineString>
<gml:LinearRing>
<gml:posList>1 2 3</gml:posList>
</gml:LinearRing>
You can use Concat:
XDocument doc = XDocument.Load(fileName);
XNamespace gml = "http://www.opengis.net/gml";
var all = doc.Descendants(gml + "Point")
.Concat(doc.Descendants(gml + "LineString"))
.Concat(doc.Descendants(gml + "LinearRing"));
For getting the values as the inner elements you can do something like this:
XDocument doc = XDocument.Load("data.xml");
XNamespace gml = "http://www.opengis.net/gml";
var all = doc.Descendants(gml + "Point")
.Select(e => new { Type = e.Name, Value = (string)e.Element(gml + "pos") })
.Concat(doc.Descendants(gml + "LineString")
.Select(e => new { Type = e.Name, Value = (string)e.Element(gml + "posList") }))
.Concat(doc.Descendants(gml + "LinearRing")
.Select(e => new { Type = e.Name, Value = (string)e.Element(gml + "posList") }));

How to add an additional filter to this XML

I have the following C# code to parse an xml document :
XDocument.Load(ConfigurationManager.AppSettings["XDocumentLoad"])
.Root
.Elements(j + "RegisteredOffenders")
.ToList()
.ForEach(element =>
{
//build out the xml namespace for the data parse
var ns = element.GetDefaultNamespace();
var role = element.Element(ns + "RoleOfPerson");
var PersonName = role.Element(ns + "PersonName");
var offender = element.Element(j + "RegisteredOffenderIdentification");
var id = element.Attribute(s + "id").Value;
//This is an inner loop that gets all the addresss for a person and writes the info to the temp strings declared above.
element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Where(a => a.Element(se + "offenderIdRef").Value == id).ToList().ForEach(ad =>
{
string aCode = ad.Element(sc + "AddressCategoryCode").Value;
switch (aCode.ToUpper())
{
case "TEMP":
string TempAddressCode = ad.Element(sc + "AddressCategoryCode").Value;
string TempStreet = ad.Element(ns + "LocationStreet").Element(ns + "StreetFullText").Value;
string Tempcity = ad.Element(ns + "LocationCityName").Value;
string Tempstate = ad.Element(sc + "LocationUSStateCode").Value;
string TempzipOne = ad.Element(ns + "LocationPostalCode").Value;
string TempzipTwo = ad.Element(ns + "LocationPostalExtensionCode").Value;
TempLocation = string.Format("{0},{1},{2},{3},{4}", TempStreet, Tempcity, TempStreet, TempzipOne, TempzipTwo);
break;
case "PERM":
string PermAddressCode = ad.Element(sc + "AddressCategoryCode").Value;
string PermStreet = ad.Element(ns + "LocationStreet").Element(ns + "StreetFullText").Value;
string Permcity = ad.Element(ns + "LocationCityName").Value;
string PermCounty = ad.Element(sc + "LocationNonFLCounty").Value;
string Permstate = ad.Element(sc + "LocationUSStateCode").Value;
string PermzipOne = ad.Element(ns + "LocationPostalCode").Value;
string PermzipTwo = ad.Element(ns + "LocationPostalExtensionCode").Value;
PermLocation = string.Format("{0},{1},{2},{3},{4},{5}", PermStreet, Permcity, PermCounty, Permstate, PermzipOne, PermzipTwo);
break;
case "TRANS":
string TransAddressCode = ad.Element(sc + "AddressCategoryCode").Value;
string TransStreet = ad.Element(ns + "LocationStreet").Element(ns + "StreetFullText").Value;
string Transcity = ad.Element(ns + "LocationCityName").Value;
string Transstate = ad.Element(sc + "LocationUSStateCode").Value;
string TranszipOne = ad.Element(ns + "LocationPostalCode").Value;
string TranszipTwo = ad.Element(ns + "LocationPostalExtensionCode").Value;
TransLocation = string.Format("{0},{1},{2},{3},{4}", TransStreet, Transcity, TransStreet, TranszipOne, TranszipTwo);
break;
}
}
);
I now need to add to the linq query to further filter the data. The data point that is now being filtered is the county information in the address portion of the the xml. I have tried using this code but it will not compile.
XDocument.Load(ConfigurationManager.AppSettings["XDocumentLoad"])
.Root
.Elements(j + "RegisteredSexOffender")
.ToList()
.ForEach(element =>
{
//build out the xml namespace for the data parse
var ns = element.GetDefaultNamespace();
var role = element.Element(ns + "RoleOfPerson");
var PersonName = role.Element(ns + "PersonName");
var offender = element.Element(j + "RegisteredOffenderIdentification");
var id = element.Attribute(s + "id").Value;
//This is an inner loop that gets all the addresss for a person and writes the info to the temp strings declared above.
element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Where(a => a.Element(se + "offenderIdRef").Value == id) && element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Elemetns(sc + "LocationNonFLCounty").Value=="ORANGE").ToList().ForEach(ad =>
{
As you can seen I tried adding an and clause to the linq statement with no luck.
I was able to get this line of code to compile but now I am not getting any records
element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Where(a => a.Element(se + "offenderIdRef").Value == id && a.Element(sc + "LocationNonFLCounty").Value == "Orange").ToList().ForEach(ad =>
You need to learn to segment your code. It is very hard to read a one-liner like that.
element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Where(a => a.Element(se + "offenderIdRef").Value == id).ToList().ForEach(ad =>
// this line has 334 characters...
element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Where(a => a.Element(se + "offenderIdRef").Value == id) && element.Document.Root.Element(se + "SopsOffenderAddressList").Elements(se + "SopsOffenderAddress").Elemetns(sc + "LocationNonFLCounty").Value=="ORANGE").ToList().ForEach(ad =>
This is the 2nd line, the one you are trying to modify :
element.Document.Root
.Element(se + "SopsOffenderAddressList")
.Elements(se + "SopsOffenderAddress")
.Where(a => a.Element(se + "offenderIdRef").Value == id)
&& // this is not how you 'add' another 'filter'(where clause)
element.Document.Root
.Element(se + "SopsOffenderAddressList")
.Elements(se + "SopsOffenderAddress")
.Elemetns(sc + "LocationNonFLCounty").Value=="ORANGE") // I think you mean to compare a *single* element to "ORANGE"
.ToList().ForEach(ad =>
You need to chain your where clause or combine them within the same where clause :
element.Document.Root
.Element(se + "SopsOffenderAddressList")
.Elements(se + "SopsOffenderAddress")
// chaining (pick one)
.Where(a => a.Element(se + "offenderIdRef").Value == id)
.Where(a => a.Element(sc + "LocationNonFLCounty").Value == "ORANGE")
// combined (pick one)
.Where(a =>
a.Element(se + "offenderIdRef").Value == id &&
a.Element(sc + "LocationNonFLCounty").Value == "ORANGE")
.ToList().ForEach(ad =>

Get List of Styles

I'm trying to get a List of Styles in the following xml file using xdoc and LINQ.
<?xml version="1.0" encoding="UTF-8"?>
<kml>
<Document>
<Style id="style62">
<IconStyle>
<Icon>
<href>http://maps.gstatic.com/mapfiles/ms2/micons/yellow-dot.png</href>
</Icon>
</IconStyle>
</Style>
</Document>
</kml>
I cannot get my syntax right in order to get the ID="style62" AND also the value within href in the same LINQ select, can anyone help?
var styles = xdoc.Descendants(ns + "Style")
.Select(s => new
{
//HELP!?!
//E.G
//
//id = s.something (style62)
//href = s.something (url)
}).ToList();
if you are talking about a kml file like here https://developers.google.com/kml/documentation/KML_Samples.kml
then below code should work. The problem here is that every "Style" does not contain "href" tag.
var xDoc = XDocument.Parse(xml);
XNamespace ns = "http://www.opengis.net/kml/2.2";
var items = xDoc.Descendants(ns + "Style")
.Select(d =>
{
var h = d.Descendants(ns + "href").FirstOrDefault();
return new
{
Id = d.Attribute("id").Value,
Href = h == null ? null : h.Value
};
})
.ToList();
With a simple extension method, you can simplify the query
XNamespace ns = "http://www.opengis.net/kml/2.2";
var items = xDoc.Descendants(ns + "Style")
.Select(d => new
{
Id = d.Attribute("id").Value,
HRef = d.Descendants(ns + "href").FirstOrDefault()
.IfNotNull(h=>h.Value)
})
.ToList();
public static class S_O_Extensions
{
public static S IfNotNull<T, S>(this T obj,Func<T,S> selector)
{
if (obj == null) return default(S);
return selector(obj);
}
}
Something like this should work:
xdoc.Descendants(ns + "Style")
.Select(s => new
{
id = s.Attribute("id").Value,
href = s.Element("IconStyle")
.Element("Icon")
.Element("href")
.Value
});
Run this through LinqPad:
XDocument doc = XDocument.Parse("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<kml>" +
"<Document>" +
"<Style id=\"style62\">" +
"<IconStyle>" +
"<Icon>" +
"<href>http://maps.gstatic.com/mapfiles/ms2/micons/yellow-dot.png</href>" +
"</Icon>" +
"</IconStyle>" +
"</Style>" +
"</Document>" +
"</kml>");
var styles = from document in doc.Root.Elements("Document")
from style in document.Elements("Style")
where style.Attribute("id").Value == "style62"
select new
{
StyleElement = style,
Href = style.Element("IconStyle").Element("Icon").Element("href").Value
};
styles.Dump();
you can use linq like
var items = doc.Descendants("field")
.Where(node => (string)node.Attribute("name") == "Name")
.Select(node => node.Value.ToString())
.ToList();

Categories