為 XmlNode.SelectNodes 加上排序功能

  • 1750
  • 0

為 XmlNode.SelectNodes 加上排序功能

測試資料

<Item a='1' b='5' c='9' m='9'/>
<Item a='2' b='6' c='9' m='9'/>
<Item a='3' b='7' c='9' m='9'/>
<Item a='4' b='8' c='9' m='1'/>
<Item a='5' b='9' c='9' m='1'/>
<Item a='6' b='0' c='5' m='1'/>
<Item a='7' b='1' c='5' m='1'/>
<Item a='8' b='2' c='5' m='5'/>
<Item a='9' b='3' c='5' m='5'/>
<Item a='0' b='4' c='5' m='5'/>
<Item a='A' b='5' c='5' m='5'/>
</Config>

測試結果

1. 依 c 及 b 屬性排序

2. b 屬性改由大到小排序

a=6 b=0 c=5 m=1
a=7 b=1 c=5 m=1
a=8 b=2 c=5 m=5
a=9 b=3 c=5 m=5
a=0 b=4 c=5 m=5
a=A b=5 c=5 m=5
a=1 b=5 c=9 m=9
a=2 b=6 c=9 m=9
a=3 b=7 c=9 m=9
a=4 b=8 c=9 m=1
a=5 b=9 c=9 m=1

c,-b
a=A b=5 c=5 m=5
a=0 b=4 c=5 m=5
a=9 b=3 c=5 m=5
a=8 b=2 c=5 m=5
a=7 b=1 c=5 m=1
a=6 b=0 c=5 m=1
a=5 b=9 c=9 m=1
a=4 b=8 c=9 m=1
a=3 b=7 c=9 m=9
a=2 b=6 c=9 m=9
a=1 b=5 c=9 m=9

測試程式

{
    var xml = new XmlDocument();
    xml.LoadXml(@"<Config>
<Item a='1' b='5' c='9' m='9'/>
<Item a='2' b='6' c='9' m='9'/>
<Item a='3' b='7' c='9' m='9'/>
<Item a='4' b='8' c='9' m='1'/>
<Item a='5' b='9' c='9' m='1'/>
<Item a='6' b='0' c='5' m='1'/>
<Item a='7' b='1' c='5' m='1'/>
<Item a='8' b='2' c='5' m='5'/>
<Item a='9' b='3' c='5' m='5'/>
<Item a='0' b='4' c='5' m='5'/>
<Item a='A' b='5' c='5' m='5'/>
</Config>");
    
    var keepRunning = true;
    while (keepRunning)
    {
        var command = Console.ReadLine();
        switch (command)
        {
            case "/exit":
            case "/quit":
                keepRunning = false;
                break;
            default:
                try
                {
                    foreach (var i in xml.SelectNodes("/Config/Item", command))
                    {
                        foreach (XmlAttribute a in i.Attributes)
                            Console.Write("{0}={1} ", a.Name, a.Value);
                        Console.WriteLine();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                break;
        }
    }
}

 

核心程式

{
    public static IEnumerable<XmlNode> SelectNodes(this XmlNode node, string xpath, string orderby)
    {
        var sorter = Sorter.Create(orderby);
        return 
            sorter == null
            ? node.SelectNodes(xpath).AsEnumerable()
            : sorter.Sort(node.SelectNodes(xpath));
    }
    private static IEnumerable<XmlNode> AsEnumerable(this XmlNodeList list)
    {
        foreach (XmlNode i in list)
            yield return i;
    }
    private static string Attrib(this XmlNode node, string name, string defaultValue)
    {
        return (node.Attributes[name] == null)
            ? defaultValue
            : node.Attributes[name].Value;
    }
    class Sorter
    {
        public string Key { get; set; }
        public bool Descending { get; set; }
        public Sorter Next { get; set; }
        private IOrderedEnumerable<XmlNode> Sort(IOrderedEnumerable<XmlNode> list)
        {
            var flow = (Next != null ? 0x10 : 0x00) + (Descending ? 0x01 : 0x00);
            switch (flow)
            {
                case 0x11: return Next.Sort(list.ThenByDescending(o => o.Attrib(Key, "")));
                case 0x10: return Next.Sort(list.ThenBy(o => o.Attrib(Key, "")));
                case 0x01: return list.ThenByDescending(o => o.Attrib(Key, ""));
                case 0x00: return list.ThenBy(o => o.Attrib(Key, ""));
            }
            throw new Exception("!!!");
        }
        public IEnumerable<XmlNode> Sort(XmlNodeList nodes)
        {
            var flow = (Next != null ? 0x10 : 0x00) + (Descending ? 0x01 : 0x00);
            switch (flow)
            {
                case 0x11: return Next.Sort(nodes.AsEnumerable().OrderByDescending(o => o.Attrib(Key, "")));
                case 0x10: return Next.Sort(nodes.AsEnumerable().OrderBy(o => o.Attrib(Key, "")));
                case 0x01: return nodes.AsEnumerable().OrderByDescending(o => o.Attrib(Key, ""));
                case 0x00: return nodes.AsEnumerable().OrderBy(o => o.Attrib(Key, ""));
            }
            return null;
        }
        public static Sorter Create(string orderby)
        {
            if (string.IsNullOrEmpty(orderby)) return null;
            var fields = orderby.Split(',');
            var list = new List<Sorter>();
            foreach (var i in fields)
            {
                var s = i.Trim();
                var desc = s.StartsWith("-");
                var key = desc ? s.Substring(1) : s;
                if (string.IsNullOrEmpty(key)) continue;
                list.Add(new Sorter { Key = key, Descending = desc });
            }
            for (int i = 1; i < list.Count; i++)
                list[i - 1].Next = list[i];
            if (list.Count > 0) return list[0];
            return null;
        }
    }
}