Zorge
Zorge

Reputation: 39

Getting data from xml hierarchically

I have this xml:

<folders>
  <Folder>
    <Folder_name>test</Folder_name>
    <Number_of_files>2</Number_of_files>
    <File>
      <File_name>DTLite4461-0327</File_name>
      <File_size_in_bytes>14682176</File_size_in_bytes>
    </File>
    <File>
      <File_name>TeamViewer_Setup-ioh</File_name>
      <File_size_in_bytes>11057224</File_size_in_bytes>
    </File>
  </Folder>
  <Folder>
    <Folder_name>podFolder1</Folder_name>
    <Number_of_files>1</Number_of_files>
    <File>
      <File_name>npp.6.9.1.Installer</File_name>
      <File_size_in_bytes>4203840</File_size_in_bytes>
    </File>
  </Folder>
  <Folder>
    <Folder_name>podFolder2</Folder_name>
    <Number_of_files>1</Number_of_files>
    <File>
      <File_name>d-470sqe</File_name>
      <File_size_in_bytes>2582112256</File_size_in_bytes>
    </File>
  </Folder>
</folders>

I want to print it in grid view which has 3 columns: File name, File size and Parent folder name.

I can get all data from all nodes, but I can't get to connect file names to appropriate parent folder name and appropriate size

I tried like this:

XmlDocument doc = new XmlDocument();
doc.Load(xPath);

XmlNodeList folderNodes = doc.SelectNodes(@"/folders/Folder");
int brojac = 0;

foreach (XmlNode folderNode in folderNodes)
{
    XmlNodeList fileNameNodes = doc.SelectNodes(@"/folders/Folder/File/File_name");
    XmlNodeList fileSizeNodes = doc.SelectNodes(@"/folders/Folder/File/Size");

    foreach (XmlNode fileName in fileNameNodes)
    {
        dgvXML.Rows.Add(fileName.InnerText, folderNode.InnerText, "");
    }
}

With this code it prints out parent folder name correctly, but gets all files each time and I cant connect it to file size.

I want to get something like this in grid view:

> File name ----------- Parent folder name ------ File size
> DTLite4461-0327       test                      14682176
> TeamViewer_Setup-ioh  test                      11057224
> npp.6.9.1.Installer   podFolder1                4203840       
> d-470sqe              podFolder2                2582112256

Which is the best way to do this?

Upvotes: 2

Views: 102

Answers (3)

Maciej Los
Maciej Los

Reputation: 8591

Well... You should re-think your xml structure, because File is not inside "grouping" element, such as Files. Xml structure should look like:

Folders
    +-Folder
      +-Files (you missed that)
        +-File

Of course, there's a way to workaround that, but requires to use XDocument class + LiqToXml instead of XmlDocument.

Take a look at example:

string xcontent = @"<?xml version='1.0' ?>..."; //replace ... with xml content
//i decided to not post entire content of xml due to clarity of code

XDocument xdoc = XDocument.Parse(xcontent);
var data = xdoc.Descendants("Folder")
                .Select(x=> new
                    {
                        FolderName = x.Element("Folder_name").Value,
                        Files = x.Descendants("File")
                         .Select(a=>
                             Tuple.Create(
                               a.Element("File_name").Value,
                               a.Element("File_size_in_bytes").Value)
                          ).ToList()
                    })
                .SelectMany(x=>x.Files.
                   Select(y=> new
                     {
                        FolderName =x.FolderName,
                        FileName = y.Item1,
                        FileSize=y.Item2
                     }))
                .ToList();

Result:

FolderName FileName             FileSize
test       DTLite4461-0327      14682176 
test       TeamViewer_Setup-ioh 11057224 
podFolder1 npp.6.9.1.Installer  4203840 
podFolder2 d-470sqe             2582112256 

What date query does?

First select statement gets the folder name and the list of files belongs to that folder. This way:

FolderName | Files(a list)
---------------------------------------------------
test       | Item1(FileName)      Item2(FileSize)
           |--------------------------------------
           | DTLite4461-0327      14682176 
           | TeamViewer_Setup-ioh 11057224 
----------------------------------------------------
...        | ... (and so on)

Second select statement (SelectMany) gets above data and transpose them into destination resultset.

Try!

Upvotes: 1

jdweng
jdweng

Reputation: 34419

Try xml linq :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        const string FILENAME = @"c:\temp\test.xml";
        public Form1()
        {
            InitializeComponent();
            DataTable dt = new DataTable();
            dt.Columns.Add("File Name", typeof(string));
            dt.Columns.Add("File Size", typeof(string));
            dt.Columns.Add("Parent", typeof(string));

            XDocument doc = XDocument.Load(FILENAME);
            foreach (XElement folder in doc.Descendants("Folder").AsEnumerable())
            {
                string folder_name = folder.Element("Folder_name").Value;
                foreach (XElement file in folder.Descendants("File").AsEnumerable())
                {
                    dt.Rows.Add(new object[] { 
                        file.Element("File_name").Value,
                        file.Element("File_size_in_bytes").Value,
                        folder_name
                    });

                }
            }
            dataGridView1.DataSource = dt;
        }
    }
}

Upvotes: 0

Gaspa79
Gaspa79

Reputation: 5689

Well since you asked the best way to do this I'd suggest using the paste special function which will make your life incredibly easy.

Basically you copy a sample of your xml, create a class using paste special, and deserialize an object or array of objects using an xmlserializer. It's all perfectly explained on the msdn link. You'll love it.

EDIT since you're having problems:

To deserialize do:

using (XmlSerializer serializer = new XmlSerializer(typeof(folders)))
{
    StreamReader myReader = new StreamReader(path_to_xml_goes_here);
    folders foldersObject = (folders)serializer.Deserialize(myReader);
    // Do stuff with the objects here
}

The XML class:

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class folders
{

    private foldersFolder[] folderField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Folder")]
    public foldersFolder[] Folder
    {
        get
        {
            return this.folderField;
        }
        set
        {
            this.folderField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class foldersFolder
{

    private string folder_nameField;

    private byte number_of_filesField;

    private foldersFolderFile[] fileField;

    /// <remarks/>
    public string Folder_name
    {
        get
        {
            return this.folder_nameField;
        }
        set
        {
            this.folder_nameField = value;
        }
    }

    /// <remarks/>
    public byte Number_of_files
    {
        get
        {
            return this.number_of_filesField;
        }
        set
        {
            this.number_of_filesField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("File")]
    public foldersFolderFile[] File
    {
        get
        {
            return this.fileField;
        }
        set
        {
            this.fileField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class foldersFolderFile
{

    private string file_nameField;

    private uint file_size_in_bytesField;

    /// <remarks/>
    public string File_name
    {
        get
        {
            return this.file_nameField;
        }
        set
        {
            this.file_nameField = value;
        }
    }

    /// <remarks/>
    public uint File_size_in_bytes
    {
        get
        {
            return this.file_size_in_bytesField;
        }
        set
        {
            this.file_size_in_bytesField = value;
        }
    }
}

Upvotes: 1

Related Questions