开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜