Reputation: 39
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
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
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
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