blah blah blah is here! blah blah » Close

up1down
link

If I use this code to create a new xml file:

new XDocument(
new XElement("user",
new XElement("name", textBox1.Text),
new XElement("age", textBox2.Text),
new XElement("rollNo", textBox3.Text)
)
).Save("gsv.xml");


Then how should I be adding the new data below the existing file? How to append this file?

last answered one year ago

14 answers

link

I'd do all that something like this:

using System.IO;
using System.Xml.Serialization;

// within form class

private Users users;
private void Form1_Load(object sender, EventArgs e)
{
XmlSerializer xs = new XmlSerializer(typeof(Users));
string filePath = @"c:\myfiles\users.xml"; // or whatever it's called
using (Stream s = File.OpenRead(filePath))
{
users = (Users)xs.Deserialize(s);
}
listBox1.DataSource = users.UserList;
listBox1.DisplayMember = "Name";
listBox1.SelectedIndex = 0;
}

private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex != -1)
{
textBox1.Text = users.UserList[listBox1.SelectedIndex].Department;
}
}

// classes needed for XML deserialization

public class User
{
private string department;
private string name;
private string empno;

[XmlElement("department")]
public string Department
{
get { return department; }
set { department = value; }
}

[XmlElement("name")]
public string Name
{
get { return name; }
set { name = value; }
}

[XmlElement("empno")]
public string EmpNo
{
get { return empno; }
set { empno = value; }
}
}

[XmlRoot("users")]
public class Users
{
[XmlElement("user")]
public List<User> UserList = new List<User>();
}

up2down
link

If you wanted to append a new element, email say, to the root element (user) and then resave the XML file, you could do it like this:

var doc = XDocument.Load("gsv.xml");
var xe = new XElement("email", textBox4.Text);
doc.Root.Add(xe);
doc.Save("gsv.xml");


EDIT - See comments below

If you want to add new users then, when you create the file, you need to have a different root element - users, say - to which you can then append as many user child elements as you like:
// to create file
new XDocument(
new XElement("users",
new XElement("user",
new XElement("name", textBox1.Text),
new XElement("age", textBox2.Text),
new XElement("rollNo", textBox3.Text)
)
)
).Save("gsv.xml");

// to append a new user having reset the textboxes
var doc = XDocument.Load("gsv.xml");
var xe = new XElement("user",
new XElement("name", textBox1.Text),
new XElement("age", textBox2.Text),
new XElement("rollNo", textBox3.Text)
);
doc.Root.Add(xe);
doc.Save("gsv.xml");


SECOND EDIT - explaining 'var'

'var' is a new keyword introduced in C# 3.0. You don't need to import any specific namespace in order to use it.

It tells the compiler to infer the type of a local variable from what you're assigning to it. So the line:

var doc = XDocument.Load("gsv.xml");

means that 'doc' is inferred to be of type XDocument because that's what XDocument.Load returns. It's therefore precisely equivalent to this line but avoids the repetition:
XDocument doc = XDocument.Load("gsv.xml");

In this situation its use is optional; if you don't like it - and many folks don't - stick with the second form.

However, it has to be used when you're declaring an 'anonymous type'. For example:
var v = new{Name = "Fred", Age = 30};
Console.WriteLine("{0} is aged {1}", v.Name, v.Age);

Apart from the declaration of anonymous types, I only use 'var' myself to avoid repetition i.e. where what's on the RHS of the '=' sign includes the type of the variable. I don't like to see it used in this scenario:
var i = 3; // infers 'i' to be of type int

gsvirdi
412

Wow thx I had still not reached this editing of the same field. My xml was having data (name - textBox1, department - textBox2, Extn - textBox2). I was asking that after entering 1 name into the xml.... if I enter another name then it should be added in the end of the existing xml file. . . . I'm talking about entering all these textBox1,2,3 and adding the data in the end of existing xml file.

vulpes
17279

Please see my edit above.

gsvirdi
412

var doc ......... this var is what? do i need to include any namespace for this?

vulpes
17279

Please see my second edit above.

gsvirdi
412

Somehow this was not working in the office. Some problem with VStudio I think. It was not able to understand "var"..... I'm at home and waiting for code to get downloaded in mail, due to some internet prob the mails are not getting downloaded. But still I will accept the answer as best answer bcoz of the explanation u wrote. Even a person like me was able to understand means that U have explained it wonderfully. Thx for always taking pains to reply with a wonderful solution. :)

vulpes
17279

The only reason I can think of why 'var' wouldn't work is if you're using VS 2005 (C# 2.0) in the office rather than VS 2008. However, in that case, LINQ to XML (XDocument, XElement etc) wouldn't work either so I'm a bit puzzled but, never mind, you can always write the types in full.

gsvirdi
412

Right I'm having VS 2005 Express edition with Framework 3.0 in office computer. I downloaded and installed a patch from Microsoft.com which added xml.linq into the list of COMs. I had been asking the admin to reinstall the application & this patch. But silly fellows are not listening. Even the dynamic help (suggestions while typing) have been affected. Prob was that other computers in office are still working in stone age so I can't go-on to add higher frameworks/versions to develop something which won't work on other computers in the network.

up0down
link

Not able to add comment, so I'm writing an answer.....

I'm planning to create something like department's news buletin where I can post some news like vendor presentations, departmental meeting, or other departmental communications.

Someday there might not be any news, but moreover I wanna display the news date-wise in a table. Something like this

Date: 09/Mar/10
News5 Some news.
News4 Some news.
News3 Some news.
News2 Some news.
News1 Some news.

Date: 07/Mar/10
News3 Some news.
News2 Some news.
News1 Some news.

, and I'm not able to create an xml which will easily help to create such an xsl to format. Biggest prob is to sort the xml depending on date tag.

data.xml I've created was:-
<news>
<date>
<on>08-mar-2010</on>
<time>10:30</time>
<title>News 1</title>
<item>News item 1 goes in here.</item>
</date>
<date>
<on>08-mar-2010</on>
<time>10:32</time>
<title>News 2</title>
<item>News item 2 goes in here.</item>
</date>
<date>
<on>08-mar-2010</on>
<time>10:32</time>
<title>News 1</title>
<item>News item 1 goes in here.</item>
</date>
<date>05-mar-2010
<on>05-mar-2010</on>
<time>13:32</time>
<title>News 1</title>
<item>News item 1 goes in here.</item>
</date>
</news>

vulpes
17279

Do you mean that the news items won't necessarily be added to the XML file in chronological order? If so, then as long as there's a date attribute for each item, you could use XSL to sort them by including an 'order-by' attribute. See http://msdn.microsoft.com/en-us/library/ms950734.aspx for an example.

up1down
link

If the XML file isn't so large that loading it into memory is a problem, then an easy way to do this would be to use XML serialization to load the news items into a list which you can then sort in descending order by their date and time.

Something like the following:

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

class Test
{

static void Main()
{
XmlSerializer xs = new XmlSerializer(typeof(News));
News news;

using(Stream s = File.OpenRead("data.xml"))
{
news = (News)xs.Deserialize(s);
}

// sort items in descending order by date & time
news.Items.Sort(new NewsItemComparer());

// check it worked
Console.Clear();

foreach(NewsItem item in news.Items)
{
Console.WriteLine(item.Date);
Console.WriteLine(item.Time);
Console.WriteLine(item.Title);
Console.WriteLine(item.Item);
Console.WriteLine();
}

// now print in tabular form

Console.WriteLine();
string currentDate = null;

for(int i = 0; i < news.Items.Count; i++)
{
NewsItem item = news.Items[i];

if (i == 0 || currentDate != item.Date)
{
if (i > 0) Console.WriteLine();
currentDate = item.Date;
Console.WriteLine(item.Date);
Console.WriteLine();
}

Console.WriteLine(" {0} {1} {2} ", item.Time, item.Title.Replace(" ", ""), item.Item);
}

Console.ReadKey();
}
}

public class NewsItem
{
private string date;
private string time;
private string title;
private string item;

[XmlElement("on")]
public string Date
{
get {return date;}
set {date = value;}
}

[XmlElement("time")]
public string Time
{
get {return time;}
set {time = value;}
}

[XmlElement("title")]
public string Title
{
get {return title;}
set {title = value;}
}

[XmlElement("item")]
public string Item
{
get {return item;}
set {item = value;}
}
}

[XmlRoot("news")]
public class News
{
[XmlElement("date")]
public List<NewsItem> Items = new List<NewsItem>();

}

public class NewsItemComparer : IComparer<NewsItem>
{
public int Compare(NewsItem item1, NewsItem item2)
{
DateTime dt1 = DateTime.Parse(item1.Date + " " + item1.Time);
DateTime dt2 = DateTime.Parse(item2.Date + " " + item2.Time);

// sorts in descending order
if (dt1 < dt2)
{
return 1;
}
else if (dt1 == dt2)
{
return 0;
}
return -1;
}
}

gsvirdi
412

these news items are not a gr8 thing, just a departmental utility to share news (like time/venue of department meeting, client visit, ISO audits, etc) with the remaining people in the department. I thought I'll just distribute this exe file so that ppl can see if there's any Important announcement / news for today.

up0down
link

Oh my goddddddd!!!!!!!!!!!!!!!!


If I've to know & write such a scary logic/code for fetching just two fields from an xml file, then I think u should guide me to a better way of writing the xml file. I think its more important to know the easily retrievable structure of xml file.

Okie let's say if I write my xml something like this:

<users>
<user>G.S.Virdi
<department>LTSL - C&amp;I</department>
<empno>755019</empno>
</user>
</users>

Is there a esier way to workout with xmls selections just like we do in xslt???

<xsl:for-each select="users/user">
<xsl:if test=listbox1.text>
<tr>
<td><xsl:value-of select="department"/></td>
<td><xsl:value-of select="empno"/></td>
</tr>
</xsl:if>
</xsl:for-each>



-----Previous Question-----
I'm just trying to populate the dorpdownlist to read all employee names and the selectedchangeindex should fill the department (in textBox1) of the "selected" employee (name)

No success :(

up0down
link

There are numerous ways to read an XML file but, if the file were as simple as that, then using LINQ to XML would probably be the easiest:

using System.Xml.Linq;

//...

XElement users = XElement.Load(filePath);
XElement user = users.Element("user");
string name = user.FirstNode.ToString();
string department = user.Element("department").Value;
string empno = user.Element("empno").Value;

up0down
link

I'm trying to add the feature of "Editing" the exisitng xml (in same folder) using XmlSerializer as per ur code

private void Form1_Load(object sender, EventArgs e)
{
textBox2.Text = "mm/dd/yyyy";
if (File.Exists("Issuez.xml"))
{
XmlSerializer xs = new XmlSerializer(typeof(Issues));
string filePath = @"Issuez.xml";
using (Stream s = File.OpenRead(filePath))
{
issues = (Issues)xs.Deserialize(s);
}
comboBox1.DataSource = issues.InstList;
comboBox1.DisplayMember = "cat";
comboBox1.SelectedIndex = 0;
}
}


I'm reading the description values into the winform:
if (comboBox1.SelectedIndex != -1)
{
richTextBox1.Text = issues.InstList[comboBox1.SelectedIndex].Desc;
}


What I'm wishing for is that: On Submit button, I need to check if the comboBox1.SelectedIndex exists in xml.
if (comboBox1.Text== existing tag/value in xml
// then append description, end date, last date of that .SelectedIndex tag
else
// add it in the xml file

private void button1_Click(object sender, EventArgs e)
{
string path = @"Issuez.xml";
if (File.Exists(path))
{
if (comboBox1.Text == issues.InstList[comboBox1].Cat &&
richTextBox1.Text == issues.InstList[comboBox1.SelectedIndex].Desc &&
dateTimePicker1.Text == issues.InstList[comboBox1.SelectedIndex].End &&
textBox2.Text == issues.InstList[comboBox1.SelectedIndex].Last)
MessageBox.Show("Duplicate Entry!!", "Error", MessageBoxButtons.OK);
else
{
// to append a new Issue having reset the textboxes
XDocument doc = XDocument.Load(path);
XElement xe = new XElement("issue",
new XElement("cat", comboBox1.Text),
new XElement("desc", richTextBox1.Text),
new XElement("end", dateTimePicker1.Text),
new XElement("last", textBox2.Text)
);
doc.Root.Add(xe);
doc.Save(path);
}
}

up0down
link

Try:

string path = Environment.CurrentDirectory + "\\issuez.xml";
this.webBrowser1.Url = new System.Uri(path, System.UriKind.Absolute);


EDIT

I'm sure there was a question that the above code was answering but it seems to have disappeared now!

Anyway, to deal with your last question, the code to add a new entry to the XML file would be something like this (notice that File.Create automatically overwrites an existing file):
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < issues.InstList.Count; i++)
{
if (comboBox1.Text == issues.InstList[i].Cat &&
richTextBox1.Text == issues.InstList[i].Desc &&
dateTimePicker1.Text == issues.InstList[i].End &&
textBox2.Text == issues.InstList[i].Last))
{
MessageBox.Show("Duplicate Entry!!", "Error", MessageBoxButtons.OK);
return;
}
}

Issue issue = new Issue();
issue.Cat = comboBox1.Text;
issue.Desc = richTextBox1.Text;
issue.End = dateTimePicker1.Text;
issue.Last = textBox2.Text;
issues.InstList.Add(issue);
XmlSerializer xs = new XmlSerializer(typeof(Issues));
string filePath = "Issuez.xml";
using (Stream s = File.Create(filePath))
{
xs.Serialize(s, issues);
}
}

up0down
link

EDIT: -

How can I insert the following tag into my new xml file which is created using C# code????

<?xml-stylesheet type="text/xsl" href="issuez.xsl"?>


ERROR
Error in xml when "Edit" function is called:
<?xml version="1.0" ?> 
- <issues xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <issue>
<cat>Issue 1</cat>
<desc>Some Issue comes here...</desc>
<end>3/24/2010</end>
<last>3/17/2010</last>
</issue>
- <issue>
<cat>Issue 1</cat>
<desc>Some Issue comes here... and its not going now.</desc>
<end>3/16/2010</end>
<last>3/20/2010</last>
</issue>
- <issue>
<cat>Issue 1</cat>
<desc>Some Issue comes here...</desc>
<end>3/24/2010</end>
<last>3/20/2010</last>
</issue>
</issues>


The Tag <issues> is coming wrongly as: <issues xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> in the xml generated using "Edit" code...

and I don't want to replace the .xml file. I just want that particular entry of that SelectedIndex field should be edited & replaced in the existing file.
On selectedIndexChanged I'm fetching the values of that Index into the Form:
if (comboBox1.SelectedIndex != -1)
{
richTextBox1.Text = issues.InstList[comboBox1.SelectedIndex].Desc;
dateTimePicker1.Text = issues.InstList[comboBox1.SelectedIndex].End;
textBox2.Text = issues.InstList[comboBox1.SelectedIndex].Att;
}


If the user is making any change in these existing values & presses "Edit" button then the same Tag on the selected Index should be rewritten in the xml.

Or should I delete that complete selected tag and rewrite the new information into the xml???

Any help?????

up0down
link

As far as I can see, there's no way to write a stylesheet processing instruction directly to a file using the XmlSerializer. What you can do is to serialize to a memorystream initially, load the stream into an XmlDocument, insert the processing instruction into that and then save it to the file.

Also, when using the XmlSerializer, you have no choice but to replace the entire file or to serialize to a new file - you can't insert or alter individual nodes.

However, it is possible to prevent the standard namespace references being added to the root node and I've included revised code to do that and add the stylesheet processing instruction below:

XmlSerializer xs = new XmlSerializer(typeof(Issues));
string filePath = "Issuez.xml";
var ms = new MemoryStream();
var ns = new XmlSerializerNamespaces();
ns.Add("", ""); // add empty namespace
xs.Serialize(ms, issues, ns);
var doc = new XmlDocument();
ms.Seek(0, SeekOrigin.Begin); // rewind stream to beginning
doc.Load(ms);
XmlProcessingInstruction pi;
string data = "type=\"text/xsl\" href=\"issuez.xsl\"";
pi = doc.CreateProcessingInstruction("xml-stylesheet", data);
doc.InsertBefore(pi, doc.DocumentElement); // insert before root
doc.Save(filePath);


EDIT

What I don't understand here is why the code gets rid of the namespaces on my machine but not on yours - it just changes them to something else!

Anyway, let's see if we can get rid of them by editing them out of the XmlDocument before saving to the file. Try adding the following line immediately before doc.Save(filePath):
doc.DocumentElement.Attributes.RemoveAll();

If this works, then it should get rid of the null reference exception as well because the deserialization should then be successful.

SECOND EDIT (cleaner version of above code)

// assumes 'issues' has already been altered by other code
XmlSerializer xs = new XmlSerializer(typeof(Issues));
string filePath = Environment.CurrentDirectory + "\\Issuez.xml";
var ms = new MemoryStream();
xs.Serialize(ms, issues);
var doc = new XmlDocument();
ms.Seek(0, SeekOrigin.Begin); // rewind stream to beginning
doc.Load(ms);
XmlProcessingInstruction pi;
string data = "type=\"text/xsl\" href=\"issuez.xsl\"";
pi = doc.CreateProcessingInstruction("xml-stylesheet", data);
doc.InsertBefore(pi, doc.DocumentElement); // insert before root
doc.DocumentElement.Attributes.RemoveAll(); // remove namespaces
doc.Save(filePath);

up0down
link

Thhis code had perfectly added the stylesheet code into the new xml, but the code

<issues d1p1:nil="true" xmlns:d1p1="http://www.w3.org/2001/XMLSchema-instance" />


Just like in previous xml:
<issues xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

Is giving an error. I'm not able to understand the need of insertion of this xmlns thing in <issues> tag... :(

vulpes
17279

You shouldn't now be getting any XML namespace references at all in the 'issues' node. Adding the empty namespace should remove them all and did do when I tested it.

up0down
link

SECOND EDIT
The same error thing is appearing in the Edit code also

Issue issue = new Issue();
issue.Cat = comboBox1.Text;
issue.Desc = richTextBox1.Text;
issue.End = dateTimePicker1.Text;
issue.Att = textBox2.Text;
issues.InstList.Add(issue);
XmlSerializer xs = new XmlSerializer(typeof(Issues));
string filePath = System.Environment.CurrentDirectory.ToString() + "\\Issuez.xml";
using (Stream s = File.Create(filePath))
xs.Serialize(s, issues);


But here we've not defined any DocumentElement, then how to capture/change it's Attributes in edit????

Okie let's replace the code and Add the same MemoryStream function and rewrite the XmlProcessingInstructions in the xml, but this logic behind "Edit" code is simply editing the xml file. Whereas the "Edit" button should be editing a particular TAG of the xml.

Let me explain myself.... the Page_Load code had fetched all the Titles of Issues into the DropDownList. Then the SelectedIndexChanged code fetches all related information of that Title into remaining fields of the form
richTextBox1.Text = issues.InstList[comboBox1.SelectedIndex].Desc;
dateTimePicker1.Text = issues.InstList[comboBox1.SelectedIndex].End;
textBox2.Text = issues.InstList[comboBox1.SelectedIndex].Att;
//


Now... now.... now.....
If the user makes any change in the Description or Start Date or End Date fields in the form..... Then
The respective Tag of the xml should be rewritten with this new Information.

This is what I'm meaning by "Edit" here. Whereas the code right now is adding another field in the xml.

Shall I delete the previous Tag and rewrite this as a new tag in xml? What do u say?

vulpes
17279

Please see my edit.

vulpes
17279

All I've done is to serialize 'issues' to a memory stream rather than directly to disk. I've then loaded the stream into an XmlDocument so we can edit it before finally writing it to disk. So, whenever, you're using the serializer you should be able to take the same steps to insert the stylesheet processing instruction and get rid of the standard namespaces that are otherwise added. Also, you might as well get rid of the 'XmlSerializerNamespaces' code which isn't achieving anything useful on your machine. See my second edit for a 'cleaner' version of the code.

vulpes
17279

The rationale of using XML (de)serialization in this way, is that you have an object in memory (namely 'issues') which represents the XML file on disk. If you add a new issue, then you should add that to issues.InstList. If you change an existing issue, then you should change the corresponding 'issue' object in issues.InstList. Then, when you serialize 'issues' back to the disk file, it overwrites the existing file and corresponds exactly with what's in memory. So, the last lot of code I posted is solely concerned with how to serialize the object to disk in the way you want (no namespaces etc). Before you can call that code, you need to ensure that your 'issues' object has been updated with the latest addtions, deletions, or amendments. It's as simple as that:)

up0down
link

Deleting a particular Tag from xml

My xml file is something like this:

<Parents> 
<Parent>
<Child 1>Something</Child 1>
<Child 2>Something</Child 2>
<Child 3>Something</Child 3>
<Child 4>Something</Child 4>
</Parent>
<Parent>
<Child 1>Something 1</Child 1>
<Child 2>Something 1</Child 2>
<Child 3>Something 1</Child 3>
<Child 4>Something 1</Child 4>
</Parent>
.
.
.
.
.
.
.
</Parents>


How should I delete complete <Parent> tag if (comboBox1.Text = "Something 1") ????
var doc = new XmlDocument();
doc.Load(filePath);
foreach(XmlNode node in doc.ChildNodes)
{
if (comboBox1.Text == issues.InstList[comboBox1.SelectedIndex].Cat)
if (richTextBox1.Text == issues.InstList[comboBox1.SelectedIndex].Desc &&
dateTimePicker1.Text == issues.InstList[comboBox1.SelectedIndex].End &&
textBox2.Text == issues.InstList[comboBox1.SelectedIndex].Att)
doc.RemoveChild(node);
}

up0down
link

This is off the top of my head and so may not be quite right:

for(int i = 0; i < issues.InstList.Count; i++)
{
Issue issue = issues.InstList[i];
if (comboBox1.Text == issue.Cat &&
richTextBox1.Text == issue.Desc &&
dateTimePicker1.Text == issue.End &&
textBox2.Text == issue.Att)
{
issues.InstList.RemoveAt(i);
break;
}
}
// now use same code as before to reserialize 'issues' to disk
XmlSerializer xs = new XmlSerializer(typeof(Issues));
string filePath = Environment.CurrentDirectory + "\\Issuez.xml";
var ms = new MemoryStream();
xs.Serialize(ms, issues);
var doc = new XmlDocument();
ms.Seek(0, SeekOrigin.Begin); // rewind stream to beginning
doc.Load(ms);
XmlProcessingInstruction pi;
string data = "type=\"text/xsl\" href=\"issuez.xsl\"";
pi = doc.CreateProcessingInstruction("xml-stylesheet", data);
doc.InsertBefore(pi, doc.DocumentElement); // insert before root
doc.DocumentElement.Attributes.RemoveAll(); // remove namespaces
doc.Save(filePath);

Feedback