How to query this two XML files using C#?
I’m creating a small application (a phonebook), actually I already created it using ms access as a database, but now, I’m learning XML and planning to use it as a database for this app (just for fun and educational purposes).
Here’s the diagram in my access database.
And I created two XML files with the same structure as for the two access tables.
ContactList Table
<?xml version="1.0" standalone="yes"?>
<ContactList>
<xs:schema id="ContactList" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="ContactList" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Contact">
<xs:complexType>
<xs:sequence>
<xs:element name="ContactID" type="xs:int" minOccurs="0" />
<xs:element name="Name" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<Contact>
<ContactID>1</ContactID>
<Name>Peter</Name>
</Contact>
<Contact>
<ContactID>2</ContactID>
<Name>John</Name>
</Contact>
</ContactList>
ContactNumbers Table
<?xml version="1.0" standalone="yes"?>
<ContactNumbers>
<xs:schema id="ContactNumbers" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="ContactNumbers" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Numbers">
<xs:complexType>
<xs:sequence>
<xs:element name="ContactID" type="xs:int" minOccurs="0" />
<xs:element name="Mobile" type="xs:string" minOccurs="0" />
<xs:element name="Office" type="xs:string" minOccurs="0" />
<xs:element name="Home" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<Numbers>
<ContactID>1</ContactID>
<Mobile>+63-9277-392607</Mobile>
<Office>02-890-2345</Office>
<Home>0</Home>
</Numbers>
<Numbers>
<ContactID>2</ContactID>
<Mobile>+62-9277-392607</Mobile>
<Office>02-890-2345</Office>
<Home>1</Home>
</Numbers>
</ContactNumbers>
This is how my simple app should look like:
In my original app, I used INNER JOIN statement to retrieve the contact numbers of a particular contact. But now, I have no idea how to do it since I’m using 2 XML files as the tables (corresponding to th开发者_开发百科e two ms access tables). Is it still possible to query and link these two XML files and achieved the same functionality as my first application (using access) version does?
For now, this is what I only have:
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;
namespace TestXML
{
public partial class Form1 : Form
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
DataSet ds = new DataSet();
DataView dv = new DataView();
public Form1()
{
InitializeComponent();
}
private void btnBrowse_Click(object sender, EventArgs e)
{
try
{
openFileDialog1.Filter = "XML Document (*.xml)|*.xml";
openFileDialog1.FileName = "";
openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
txtDirectory.Text = openFileDialog1.FileName;
btnLoad.Enabled = true;
}
}
catch (Exception x)
{
btnLoad.Enabled = false;
MessageBox.Show("Something went wrong! \n" + x.Message, "Ooops!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
private void btnLoad_Click(object sender, EventArgs e)
{
dgContactList.DataSource = LoadXML();
}
private DataView LoadXML()
{
try
{
ds.Clear();
ds.ReadXml(txtDirectory.Text, XmlReadMode.ReadSchema);
dv = ds.Tables[0].DefaultView;
lblStatus.Text = "XML is loaded successfully";
}
catch (Exception x)
{
MessageBox.Show("Something went wrong! \n" + x.Message, "Ooops!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
lblStatus.Text = "";
}
return dv;
}
}
}
Here is my solution(MainList
is your 1st XML and DetailedList
is the second.)
using System;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog OpenFD = new OpenFileDialog();
OpenFD.InitialDirectory = Application.StartupPath;
OpenFD.FileName = "";
OpenFD.ShowDialog();
if (OpenFD.FileName == "")
return;
textBox1.Text = OpenFD.FileName;
ReadXMLFile(OpenFD.FileName);
}
private void ReadXMLFile(String strFileName)
{
var X = XDocument.Load(strFileName).Descendants("Contact").Select(N => new
{
ID = N.Element("ContactID").Value,
Name=N.Element("Name").Value
});
foreach (var XX in X)
{
dataGridView1.Rows.Add(XX.ID, XX.Name);
}
}
private void dataGridView1_RowHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
String St = dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
var Data = XDocument.Load(Application.StartupPath + "\\DetailedList.xml").Descendants("Numbers")
.Where(X=>X.Element("ContactID").Value ==St)
.Select(N => new
{
Mobile = N.Element("Mobile").Value,
Office = N.Element("Office").Value,
Home = N.Element("Home").Value
});
dataGridView2.Rows.Clear();
foreach (var X in Data)
{
dataGridView2.Rows.Add(X.Mobile,X.Office,X.Home);
}
}
}
}
Output
Add the necessary property to both the gridViews
I've created the columns
at the design time.
And Instead of foreach
we can wse LAMBDA Expression
Hope you will understand.... Please let me know if you have any isses.
Enjoy!!!!!
I personally don't like to work with DataView, DataTable and so on. I would created classes that correspond to your XML data. Eg. Contact and Numbers. Then I would read in the data using XDocument with XML LINQ syntax. You create a collection of contacts that you are going to set on the first GridView, and after clicking you just read the selected object from the collection and set the data on the second GridView.
No need for inner joins, everything is defined in classes and clearly better readable. Just an opinion.
EDIT:
More Infos:
Create classes that represent your data, e.g. Contact, Numbers
Read about XDocument at MSDN.
Example:
XDocument contactDoc = XDocument.Load(m_helpTopicFile);
var contacts = from xmlTopic in contactDoc.Descendants("Contact")
select new Contact
{
Id = int.Parse(xmlTopic.Element("ContactID").Value, CultureInfo.InvariantCulture),
Name = xmlTopic.Element("name").Value,
};
Then set this as the DataSource by using contacts.ToList()
精彩评论