0

I try to put my XML File into a Dictionary with Generic List. How I can merge correct the query List to my dictionary with correct key? Instead of .ToList() .ToDictionary is not possible?

<?xml version="1.0"?>
<customers>
  <cust ID="1" DeviceID="1" Name="Bob" Latitude="10" Longitude="58" Device="HW1.0"> </cust>
  <cust ID="2" DeviceID="2" Name="Jack" Latitude="28" Longitude="20" Device="HW2.2"> </cust>
</customers>

//XML attribute Name is Dict Key

public class Customers
{
    public int Longitude { get; set; }
    public int Latitude { get; set; }
    public int DeviceID { get; set; }
    public string Device { get; set; }
}

class Program
{

    private static Dictionary<string, List<Customers>> ReadXmlToDict(string filename)
    {
        // Should be Key = Xml Attribute Name Value, List of class 
        Dictionary<string, List<Customers>> dict = new Dictionary<string, List<Customers>>();

        XDocument xdoc = XDocument.Load(filename);
        var querylist = (from row in xdoc.Descendants("cust")
                         select new Customers()
                         {
                             //Name = (string)row.Attribute("Name"),  // Wrong here should be the Dic key
                             DeviceID = (int)row.Attribute("DeviceID"), // list value
                             Longitude = (int)row.Attribute("Longitude"),  // list value
                             Latitude = (int)row.Attribute("Latitude"), // list value
                             Device = (string)row.Attribute("Device")  // list value
                         }).ToList();

        return null; // null for test To.List and not Dict
    }
Tyress
  • 3,573
  • 2
  • 22
  • 45
Shazter
  • 305
  • 1
  • 4
  • 17
  • Maybe something like this `(from row in xdoc.Descendants("cust")).ToDictionary(k => (string)row.Attribute("ID"), (v => select new Customers() { }).ToList());`? – Tim Mar 03 '16 at 23:44
  • Thank you, but i have stil problems to make the linq query correct to Dictionary,List>. The syntax is still wrong, but your help gave me a idea. I will try. – Shazter Mar 04 '16 at 00:36

2 Answers2

2

This is how I would implement it, I think it accomplishes what you're looking for. You have a class called Customers and then want to store a list of those customers with a single key...I don't follow that logic.

I created a class called Customer, which houses the information for a singular customer. Since you're returning a Dictionary<string, Customer>, where the string is the unique attribute Name in the xml, there is no use case for the value of your dictionary being a List<Customer>. Perhaps if you have multiple customers under the same name, you would use this, but why not then make the key the (I assume) truly unique identifier, the DeviceID?

namespace TestConsole
{
    class Customer
    {
        public int DeviceID;
        public int Longitude;
        public int Latitude;
        public string Device;
    }
    class Program
    {

        private static Dictionary<string, Customer> ReadXmlToDictionary(string filename)
        {
            var dict = new Dictionary<string, Customer>();

            var doc = XDocument.Load(@"C:\test.xml");

            dict = doc.Descendants("cust")
                .ToDictionary(
                    row => (string)row.Attribute("Name"),
                    row => new Customer {
                        Device = (string)row.Attribute("Device"),
                        DeviceID = (int)row.Attribute("DeviceID"),
                        Latitude = (int)row.Attribute("Latitude"),
                        Longitude = (int)row.Attribute("Longitude")
                });

            return dict;
        }

        static void Main(string[] args)
        {
            ReadXmlToDictionary(null);
        }
    }
}

Image of results

EDIT: Thought the performance related answer was interesting, so decided to try it out for this single level xml (using ID as the unique identifier). Here are the results:

1019 Descendants took 0.0030257 seconds.
1019 Elements took 0.0028348 seconds.
10000 Descendants took 0.0098942 seconds.
10000 Elements took 0.0101478 seconds.
100000 Descendants took 0.0873025 seconds.
100000 Elements took 0.1223577 seconds.

EDIT: After creating your xsd, and generating a class from it, you would then use it as such:

var parsed = XDocument.Parse(doc.ToString());

var serializer = new XmlSerializer(typeof(Xsds.customers));

var typedPayload = serializer.Deserialize(doc.Root.CreateReader());

var xmlAsClass = (TestConsole.Xsds.customers)typedPayload;

dict = xmlAsClass.cust
    .ToDictionary(
        row => (int)row.ID,
        row => new Customer {
            Device = row.Device,
            DeviceID = row.DeviceID,
            Latitude = row.Latitude,
            Longitude = row.Longitude,
            Name = row.Name
        });
  • You are totally right I need no List, because each customer should be unique by name. I had a missunderstanding not only in the correct syntax. Many thanks! – Shazter Mar 04 '16 at 07:53
  • Related to my topic I have one more question. I really dont like hard coded values e.g. the Class for customers. Reuse is very low, a class based on Attributes would be nice, including generic xml Attribute names. – Shazter Mar 04 '16 at 09:31
  • You can, and should, create an xsd. You could use this if you don't want to manually create your xsd: http://www.freeformatter.com/xsd-generator.html#ad-output. Since some of these are nullable, I don't think you'll be able to use `xsd.exe` per the reasons here: http://stackoverflow.com/questions/32610770/deserialization-of-nullable-value-with-xsd-exe-generated-class, so use xsd2code or wscfblue or some other tool to generate the class. – Tyler StandishMan Mar 04 '16 at 11:09
  • Nice Benchmark! XML Schema is understandible and I have already a schema for my xml file, based on the new teached and learned features I will try the latest version based on xml schema. I think the little bit more effort helps later to reuse my classes and methods in other projects better. – Shazter Mar 04 '16 at 15:00
  • Hey @Shazter remember to mark an answer if it answered your question that way future readers will have a possible solution to their similar problems. – Tyler StandishMan Mar 26 '16 at 14:18
1

You can do it easily by using ToDictionary() extension method. But performance wise, it is far better to use Elements() method rather than Descendants(); For further reading please read this blog post: WHY (OR WHEN) YOU SHOULD/SHOULDN’T USE DESCENDANTS() METHOD

And your query will look like this:

var customersDictionary = 
    xDoc.Root
        .Elements("cust")
        .ToDictionary(xe => 
                        xe.Attribute("Name").Value, xe => 
                        new Customers
                        {
                            DeviceID = (int)xe.Attribute("DeviceID"), 
                            Longitude = (int)xe.Attribute("Longitude"),  
                            Latitude = (int)xe.Attribute("Latitude"),
                            Device = (string)xe.Attribute("Device")
                        });
Arin Ghazarian
  • 5,105
  • 3
  • 23
  • 21