Thread: LINQ: How to Read XML PARAMETER Attributes

  1. #1
    Registered User
    Join Date
    May 2010
    Posts
    2

    Question LINQ: How to Read XML PARAMETER Attributes

    Hello everyone,

    This is my first time to learn LINQ and I need to be able to query data quickly and store the information in an object. Not sure which thread to post this to, but I think C-Sharp Programming should be the correct thread.

    Anyway, I have the following string :

    Code:
    theString = @" 
    
    <MgmtSystemInfo> 
    <LocalFixeDisk> 
    <Disks Drive="C:"> 
    <Param Description="Local Fixed Disk" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param Compressed="No" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param FileSystem="NTFS" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param TotalSpace="149.05GB (160039239680bytes)" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param AvailableSpace="140.47GB (150827999232bytes)" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param PercentageAvaliable="94.24%" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param VolumeName="OS" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param VolumeSerialNumber="9C3F9B31" TimeStamp="4/26/2010 2:20:11 AM" /> 
    </Disks> 
    </LocalFixeDisk> 
    <SystemInfo> 
    <Param OSName="Microsoft Windows XP Professional" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param Version="5.1.2600 Service Pck 2 Build 2600" TimeStamps="4/26/2010 2:20:11 AM" /> 
    <Param OSManufacturer="Microsoft Corporation" TimeStamps="4/26/2010 2:20:11 AM" /> 
    <Param SN=" 1234567" TimeStamps="4/26/2010 2:20:11 AM" / 
    <Param WindowsDirectory="C:\WINDOWS" TimeStamps="4/26/2010 2:20:11 AM" /> 
    </SystemInfo> 
    <MgmtSystemInfo>"
    I need to be able to read each Param attribute from say, the <Disks> </Disks> section of this XML string.

    For instance, i want to be able to read the Description attribute in this section of the above XML :


    Code:
    <Param Description="Local Fixed Disk" TimeStamp="4/26/2010 2:20:11 AM" />
    and then programatically read the rest of the attributes in each PARAM section, say, the Compressed and FileSystem attributes, etc.

    <
    Code:
    Param Compressed="No" TimeStamp="4/26/2010 2:20:11 AM" /> 
    <Param FileSystem="NTFS" TimeStamp="4/26/2010 2:20:11 AM" />
    and so on...


    How do I do this ?

    I tried this snippet as an example but it does not work ( I get an exception when I added the commented out lines below )
    Code:
    class Param 
    { 
    public string Description { get; set; } 
    public string Compressed { get; set; } 
    public string FileSystem { get; set; } 
    public string TotalSpace { get; set; } 
    public string AvailableSpace { get; set; } 
    public string PercentageAvailable { get; set; } 
    public string Volumename { get; set; } 
    public string VolumeSerialNum { get; set; } 
    }; 
    
    public class DiskDrives 
    { 
    public string Drive { get; set; } 
    } 
    
    XDocument xdoc = Xdocument.Parse(theString); 
    
    
    List<DiskDrives> LocalDisks = 
    (from drives in xdoc.Descendants("Disks") 
    select new DiskDrives 
    { 
    Drive = drives.Attribute("Drive").Value, 
    }).ToList<DiskDrives>(); 
    
    int cnt = 0; 
    
    foreach (var drives in LocalDisks) 
    { 
    Console.WriteLine("Drive: " + drives.Drive.ToString()); 
    var Parameters = from theparam in xdoc.Descendants("Disks") 
    where (theparam.Attribute("Drive").Value == drives.Drive) 
    select new Param 
    { 
    Description = theparam.Element("Param").Attribute("Description").Value, 
    
    // Note, I had to comment the next two lines out as they were causing exceptions.
    // My intent is to capture the rest of the attributes in the above XML string
    
    // Compressed = theparam.Element("Param").Attribute("Compressed").Value, 
    //FileSystem = theparam.Element("Param").Attribute("FileSystem").Value, 
    }; 
    
    foreach (var item in Parameters) 
    { 
    Console.WriteLine("Item: " + item.Description); // I get the correct value here "Local Fixed Disk"
    Console.WriteLine("Item: " + item.Compressed); // The value should be "No" 
    Console.WriteLine("Item: " + item.FileSystem); // The value should be "NTFS"
    } 
    }
    In the above code, the commented out lines cause exceptions.

    I am able to get the Description attribute of the first <PARAM />
    section, but do not know how I can get the other attributes programatically ( e.g., the Compressed and FileSystem attributes ).

    Your help/advise regarding this matter will be highly appreciated.

  2. #2
    ...and never returned. StainedBlue's Avatar
    Join Date
    Aug 2009
    Posts
    168
    DISCLAIMER: the linq use in this example is gratuitous for demonstration purposes.

    This example will work for any number of <Disk> elements, containing any number of <Param> elements. I leave it to you to implement the same functionality for the <SystemInfo> element(s).

    Code:
    
    using System;
    using System.Text;
    using System.Linq;
    using System.Xml.Linq;
    using System.Reflection;
    using System.Collections.Generic;
    
    namespace XElementParsing
    {
        class Program
        {
            static void Main(string[] args)
            {
                XDocument xDocument = XDocument.Parse(
                "<MgmtSystemInfo>" +
                    "<LocalFixeDisk>" +
                        "<Disks Drive=\"C:\">" +
                            "<Param Description=\"Local Fixed Disk\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param Compressed=\"No\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param FileSystem=\"NTFS\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param TotalSpace=\"149.05GB (160039239680bytes)\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param AvailableSpace=\"140.47GB (150827999232bytes)\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param PercentageAvailable=\"94.24%\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param VolumeName=\"OS\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param VolumeSerialNumber=\"9C3F9B31\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                            "<Param TestParam=\"Testing...\" />" +
                        "</Disks>" +
                    "</LocalFixeDisk>" +
                    "<SystemInfo>" +
                        "<Param OSName=\"Microsoft Windows XP Professional\" TimeStamp=\"4/26/2010 2:20:11 AM\" />" +
                        "<Param Version=\"5.1.2600 Service Pck 2 Build 2600\" TimeStamps=\"4/26/2010 2:20:11 AM\" />" +
                        "<Param OSManufacturer=\"Microsoft Corporation\" TimeStamps=\"4/26/2010 2:20:11 AM\" />" +
                        "<Param SN=\" 1234567\" TimeStamps=\"4/26/2010 2:20:11 AM\" />" +
                        "<Param WindowsDirectory=\"C:\\WINDOWS\" TimeStamps=\"4/26/2010 2:20:11 AM\" />" +
                    "</SystemInfo>" +
                "</MgmtSystemInfo>");
    
                List<Disk> disks = GetDisks(xDocument).ToList();
                foreach( Disk disk in disks ) { Console.WriteLine(disk.ToString()); }
            }
    
             
    
    
            public static IEnumerable<Disk> GetDisks(XDocument xDocument)
            {
                var disks =
                    xDocument.Descendants("Disks")
                   .Select(d => new Disk()
                   {
                       Drive = d.Attribute("Drive").Value,
                       Parameters = GetDiskParameters(d)
    
                   });
    
                return disks;
            }
    
           
    
    
            public static DiskParameters GetDiskParameters(XElement xElement)
            {
                DiskParameters parameters = new DiskParameters();
    
                string[] propertyNames = parameters.GetType().GetProperties()
                                            .Select(p => p.Name)
                                            .ToArray();
    
                foreach( XElement xParam in xElement.Elements("Param") )
                {
                    if( propertyNames.Contains(xParam.FirstAttribute.Name.ToString()) )
                    {
                        PropertyInfo property = parameters.GetType().GetProperty(xParam
                                               .FirstAttribute.Name
                                               .ToString());
    
                        property.SetValue(parameters, xParam.FirstAttribute.Value, null);
                    }
                }
    
                return parameters;
            }
        }
    
        
    
    
        public class Disk
        {
            public string Drive { get; set; }
            public DiskParameters Parameters { get; set; }
    
            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(string.Format("Drive = {0}\n", Drive));
    
                foreach( PropertyInfo property in Parameters.GetType().GetProperties() )
                {
                    sb.Append(string.Format(
                        "{0} = {1}\n",
                        property.Name,
                        (string)property.GetValue(Parameters, null)
                    ));
                }
    
                return sb.ToString();
            }
        }
    
       
    
    
        public class DiskParameters
        {
            public string Description { get; set; }
            public string Compressed { get; set; }
            public string FileSystem { get; set; }
            public string TotalSpace { get; set; }
            public string AvailableSpace { get; set; }
            public string PercentageAvailable { get; set; }
            public string VolumeName { get; set; }
            public string VolumeSerialNumber { get; set; }
        }
    }
    Alternatively, we could have populated the Disk Parameters like
    Code:
    
    var disks =
                    xDocument.Descendants("Disks")
                   .Select(d => new Disk()
                   {
                       Drive = d.Attribute("Drive").Value,
                       Parameters = new DiskParameters()
                       {
                           Description = d.Elements("Param").ElementAt(0).Attribute("Description").Value,
                           Compressed = d.Elements("Param").ElementAt(0).Attribute("Description").Value,
                           //...
                       }
                   });
    but then if the number of <Param> elements in the XML changes unexpectedly, an exception will be thrown, so I use reflection (bcuz I'm a lazy typer) plus a test to see if the <Param>'s attribute is a known property.

    This means the only place I need to add code if the XML changes, is in the Disk Parameter's implementation. I'd simply add another property to accomodate.

    (BTW: I added an extra <Param> tag (has attribute: "TestParam="Testing...") to show that this example won't fail just because the number of <Param>s changes)
    Last edited by StainedBlue; 05-04-2010 at 12:59 PM.
    goto( comeFrom() );

  3. #3
    Registered User
    Join Date
    May 2010
    Posts
    2

    Thanks for your helpful response

    StainedBlue,

    Thanks for your helpful response. Much appreciated.

    In fact, your solution is safer and a better catch all solution than the one I just thought of ( see below ):

    I also thought about the problem and thought about this solution :

    I would like to share with all in this forum for your future reference ( critiques are welcome of course ).

    Again the XML string is :



    Code:
    <MgmtSystemInfo>
      <LocalFixeDisk>
        <Disks Drive="C:">
          <Param Description="Local Fixed Disk" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param Compressed="No" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param FileSystem="NTFS" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param TotalSpace="149.05GB (160039239680bytes)" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param AvailableSpace="140.47GB (150827999232bytes)" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param PercentageAvaliable="94.24%" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param VolumeName="OS" TimeStamp="4/26/2010 2:20:11 AM" />
          <Param VolumeSerialNumber="9C3F9B31" TimeStamp="4/26/2010 2:20:11 AM" />
        </Disks>
      </LocalFixeDisk>
      <SystemInfo>
        <Param OSName="Microsoft Windows XP Professional" TimeStamp="4/26/2010 2:20:11 AM" />
        <Param Version="5.1.2600 Service Pck 2 Build 2600" TimeStamps="4/26/2010 2:20:11 AM" />
        <Param OSManufacturer="Microsoft Corporation" TimeStamps="4/26/2010 2:20:11 AM" />
        <Param SN=" 1234567" TimeStamps="4/26/2010 2:20:11 AM" />
        <Param WindowsDirectory="C:\WINDOWS" TimeStamps="4/26/2010 2:20:11 AM" />
      </SystemInfo>
      </MgmtSystemInfo>
    I looked at the XML string again and I did notice that the Param sections consist of Key/Value pairs that need to be captured in some sort of Key/Val data structure ( for instance, Description is a key and "Local Fixed Disk" is the value ).

    Hence, I modified the code to look like this ( modified the class to capture the XML data ):

    ---------------------------------------------------------------------------------------------

    Code:
    public class DiskDrives 
    { 
       public string DriveName { get; set; } 
       public SortedList<string, string> DiskParams; // Intended to be Key/Val pair 
    } // End class DiskDrivesI
    I then modified the LINQ code as follows :

    ---------------------------------------------------------------------------------------------


    Code:
    XDocument xdoc = XDocument.Parse(Properties.Resources.TheString); 
    
    // Get Data for the LocalFixeDisk section --- Drive and corresponding Parameters 
    
    ListWksDrives = 
    (from drives in xdoc.Descendants("Disks") 
    select new DiskDrives{ 
       DriveName = drives.Attribute("Drive").Value, 
    }).ToList<DiskDrives>(); 
    
    // From hereon, we get the Param key/val pairs 
    
    int cnt = ListWksDrives.Count(); 
    
    foreach (var drives in ListWksDrives) 
    { 
       // Console.WriteLine("Drive: " + drives.DriveName.ToString()); 
       drives.DiskParams = new SortedList<string, string>(cnt); 
        
       foreach (XElement elem in xdoc.Descendants("Disks")) 
       { 
          var MyParams = elem.Elements("Param"); 
          foreach (XElement elem1 in MyParams) 
          { 
              Console.WriteLine(" Key/Val : " + elem1.FirstAttribute.Name + "/" + 
                                                 elem1.FirstAttribute.Value); 
              if (elem.Attribute("Drive").Value.ToString() == drives.DriveName){ 
             drives.DiskParams.Add(elem1.FirstAttribute.Name.ToString(), 
                                                         elem1.FirstAttribute.Value.ToString()); 
            } 
      } 
    } 
    }// End foreach (var drives in ListWksDrives) 
    
    Console.WriteLine("************ DISK INFO *****************"); 
    Console.WriteLine(); 
    
    for (int i=0; i < ListWksDrives.Count; i++) 
    { 
        Console.WriteLine("Disk Drive: " + ListWksDrives[i].DriveName); 
        
        foreach (KeyValuePair<string, string> keyValPair in ListWksDrives[i].DiskParams) 
        { 
               Console.WriteLine("Key:" + keyValPair.Key + " Value: " + keyValPair.Value); 
        } 
    }
    Last edited by SeekAndFind; 05-06-2010 at 10:32 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 03-04-2005, 02:46 PM
  2. Unknown Memory Leak in Init() Function
    By CodeHacker in forum Windows Programming
    Replies: 3
    Last Post: 07-09-2004, 09:54 AM
  3. Read Array pro!!Plz help!!
    By Supra in forum C Programming
    Replies: 2
    Last Post: 03-04-2002, 03:49 PM
  4. Serial Communications in C
    By ExDigit in forum Windows Programming
    Replies: 7
    Last Post: 01-09-2002, 10:52 AM
  5. Help! Can't read decimal number
    By Unregistered in forum C Programming
    Replies: 2
    Last Post: 09-07-2001, 02:09 AM