What is the best way to sort xml elements by attribute values in c#/.net 3.5
I have a XML file with data like:
<Details>
<TableDef>
<Column Name="Calldate" HeaderName="Calldate" SourceColumnName="Calldate" IsHidden = "false" Position="4" />
<Column Name="Issue" HeaderName="Issues" SourceColumnName="Issues" IsHidden = "false" Position="3" />
<Column Name="ParamortizedValue" HeaderName="paramortizedvalue" SourceColumnName="Value" IsHidden = "false" Position="1" />
<Column Name="PercentBondTotal" HeaderName="percentbondtotal" SourceColumnName="Percentage" IsHidden = "false" Position="2" />
</TableDef>
</Details>
I was wondering what would be the best and efficient way to filter and sort the elements by an attribute so that I can use that sorted elements further in my code?
In above example, I want to filter by "IsHidden = false" and then sort the elements by "Position" attribute.
I have the following code that will read all the elements without any sort:
var doc = XDocument.Load("data.xml");
var nodeTableDefInfo = doc.XPathSelectElements("/Details");
var elements = nodeTableDefInfo.Elements("TableDef").Elements();
foreach (var e in elements)
{
//want to work on the sorted elements
}
Guys, Thanks for all the solutions. before I could read your solution I came up with this:
var elements = nodeTableDefInfo.Elements("TableDef").Elements();
var sortedElements = (from element in elements
where element.Attribute("IsHidden").Value == "false"
orderby element.Attribute("Position").Value ascending
select element);
foreach (var开发者_JAVA百科 e in sortedElements)
{
//work on the sorted elements
}
Now, after going through the solutions, I really liked the solution and explanation provided by GFK
. I guess I need some handling for nulls. Thanks all.
Use some more LINQ!
var sortedElements =
from element in elements
let hiddenAttribute = element.Attribute("IsHidden")
let positionAttribute = element.Attribute("Position")
where hiddenAttribute != null
&& positionAttribute != null
&& hiddenAttribute.Value == "false"
let position = int.Parse(positionAttribute)
orderby position
select element;
foreach (var e in sortedElements)
{
// ...
}
Edit: also, what I do usually with LINQ-to-XML is create an anonymous type so I can parse once and for all the attributes into strongly typed values (and handle missing or invalid values), like:
int parsedInt;
var sortedElements =
from element in elements
let hiddenAttribute = element.Attribute("IsHidden")
let positionAttribute = element.Attribute("Position")
// add other attributes here
select new
{
IsHidden = hiddenAttribute == null || hiddenAttribute.Value != "false",
Position = positionAttribute == null || !int.TryParse(positionAttribute.Value, out parsedInt)
? default(int?)
: parsedInt,
// add other parsed attributes here
};
This way, all operations is made easier, like sorting or filtering:
foreach(var element in sortedElements.Where(e => !e.IsHidden).OrderBy(e => e.Position)
{
// ...
}
var tableDefs = nodeTableDefInfo.Elements("TableDef").Elements();
foreach(var column in tableDefs.Where(x=>
x.Attributes("IsHidden") != null &&
bool.Parse(x.Attributes("IsHidden").value) == false &&
x.Attributes("Position") != null)
.OrderBy(x=>int.Parse(x.Attributes("Position"))
linq..
string str = @"
<Details>
<TableDef>
<Column Name='Calldate' HeaderName='Calldate' SourceColumnName='Calldate' IsHidden='false' Position='4' />
<Column Name='Issue' HeaderName='Issues' SourceColumnName='Issues' IsHidden='false' Position='3' />
<Column Name='ParamortizedValue' HeaderName='paramortizedvalue' SourceColumnName='Value' IsHidden = 'false' Position='1' />
<Column Name='PercentBondTotal' HeaderName='percentbondtotal' SourceColumnName='Percentage' IsHidden = 'false' Position='2' />
</TableDef>
</Details>";
XDocument xml = XDocument.Parse(str);
List<XElement> columns = (from t in xml.Descendants("TableDef").Descendants("Column") where (bool)t.Attribute("IsHidden") == false orderby (int)t.Attribute("Position") ascending select t).ToList();
Try Linq2XML.
XDocument ip = XDocument.Load(Server.MapPath("~/data.xml"));
var op = ip.Descendants("Column")
.Where(node => node.Attribute("IsHidden").Value == "false")
.OrderBy(node => int.Parse(node.Attribute("Position").Value))
.ToList();
Now, op
will be List<XElement>
. Use for
or foreach
to manipulate it.
Sample approach:
var list = new List<KeyValuePair<string, XElement>>();
foreach(var element in elements)
{
list.Add(new KeyValuePair<string,XElement>(
element.Attribute("someAttr").Value,
element));
}
var sorted = from entry in list
orderby entry.Value.Attribute("someAttr").Value
select entry;
// just test:
foreach (var entry in sorted)
{
Debug.WriteLine(String.Format("attribute value: {0} - element: {1}", entry.Key, entry.Value.ToString()));
}
We're retrieving your chosen attribute values and treat it as an index.
We create a dictionary (a list of key-value pairs to be precise, as those values may not be unique) and then we just sort the list by the key.
I assume that this attribute will be present for every XElement
- if that's not the case, you need to check for null of course.
精彩评论