I am a beginner with .NET environment.
I have a windows application with three textboxes and one button. When the user clicks on the button, i want all the textbox values to be serialized in an XML format to a file.
I tried doing it this way,
DialogResult dr = new DialogResult();
private void button1_Click(object sender, EventArgs e)
{
AddCustomer customer = new AddCustomer();
customer.textBox1.Text = textBox1.Text;
customer.textBox2.Text = textBox2.Text;
customer.textBox3.Text = textBox3.Text;
customer.textBox4.Text = textBox4.Text;
saveFileDialog1.InitialDirectory = #"D:";
saveFileDialog1.Filter = "Xml Files | *.xml";
if (saveFileDialog1.ShowDialog().Equals(DialogResult.OK))
{
SerializeToXML(customer);
}
}
public void SerializeToXML(AddCustomer customer)
{
XmlSerializer serializer = new XmlSerializer(typeof(AddCustomer));
TextWriter textWriter = new StreamWriter(#"D:\customer.xml");
serializer.Serialize(textWriter, customer);
textWriter.Close();
}
this returned system.invalidoperationexception was unhandled exception
any ideas?
Thanks,
Michael
You can't serialize controls instead you have to create Serializable component that represent TextBox values. (For more detail read MSDN article).
For instance,
[Serializable]
public class Customer
{
public string Name { get; set; }
public string Address {get;set;}
}
You shouldn't serialize the textbox object, only their values.
customer.textBox1 should be customer.text1 of type string. You then need to just assign customer.text1 = textbox1.text.
Also, mark your AddCustomer class with the [Serializable] attribute.
Edit: This is a code I just tried and it works fine. See if you can make it work in your solution.
Add new class Customer
[Serializable]
public class Customer
{
public string FullName { get; set; }
public string Age { get; set; }
}
Try to serialize it like this
Customer customer = new Customer();
customer.FullName = "John"; // or customer.FullName = textBox1.Text
customer.Age = "21"; // or customer.Age = textBox2.Text
XmlSerializer serializer = new XmlSerializer(typeof(Customer));
TextWriter textWriter = new StreamWriter(#"D:\customer.xml");
serializer.Serialize(textWriter, customer);
textWriter.Close();
After doing this, I got an xml file created with the following content.
<?xml version="1.0" encoding="utf-8"?>
<Customer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FullName>John</FullName>
<Age>21</Age>
</Customer>
Try and compare to see what you are doing wrong.
There are a lot of ways to write XML in .NET. Here is a way using XmlWriter that works for very simple content like in this case:
string text1 = /* get value of textbox */;
string text2 = /* get value of textbox */;
string text3 = /* get value of textbox */;
// Set indent=true so resulting file is more 'human-readable'
XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };
// Put writer in using scope; after end of scope, file is automatically saved.
using (XmlWriter writer = XmlTextWriter.Create("file.xml", settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("root");
writer.WriteElementString("text1", text1);
writer.WriteElementString("text2", text2);
writer.WriteElementString("text3", text3);
writer.WriteEndElement();
}
One note: you should avoid doing file operations on the UI thread as this can result in blocking behavior (e.g. the disk can be slow and cause the UI to freeze up while it writes the file, or it could be writing to a network location and hang for a while as it connects). It is best to put up a progress dialog and display a message "Please wait while file is saved..." and do the file operation in the background; a simple way is to post the background operation the thread pool using BeginInvoke/EndInvoke.
If you want to use the XmlSerializer instead, then you would follow these steps:
Create a public type to act as the root element of your document and mark it with XmlRoot.
Add elements/attributes made of either primitive/built-in types or your own public custom types which are also XML serializable, marking them with XmlElement or XmlAttribute as necessary.
To write the data out, use XmlSerializer.Serialize with an appropriate Stream, StreamWriter, or XmlWriter along with your root object.
To read the data back in, use XmlSerializer.Deseralize with an appropriate Stream, TextReader, or XmlReader, casting the return type back to your root object.
Full sample.
The type to serialize:
[XmlRoot("customer")]
public class CustomerData
{
// Must have a parameterless public constructor
public CustomerData()
{
}
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("city")]
public string City { get; set; }
[XmlElement("company")]
public string Company { get; set; }
public override string ToString()
{
return
"Name=[" + this.Name + "] " +
"City=[" + this.City + "] " +
"Company=[" + this.Company + "]";
}
}
The code to read/write the data:
// Initialize the serializer to write and read the data
XmlSerializer serializer = new XmlSerializer(typeof(CustomerData));
// Initialize the data to serialize
CustomerData dataToWrite = new CustomerData()
{
Name = "Joel Spolsky",
City = "New York",
Company = "Fog Creek Software"
};
// Write it out
XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };
using (XmlWriter writer = XmlTextWriter.Create("customer.xml", settings))
{
serializer.Serialize(writer, dataToWrite);
}
// Read it back in
CustomerData dataFromFile = null;
using (XmlReader reader = XmlTextReader.Create("customer.xml"))
{
dataFromFile = (CustomerData)serializer.Deserialize(reader);
}
Console.WriteLine(dataFromFile);
Related
I'm trying to create an Rssfeed reader which saves info about a podcast to a JSON file and I'm having trouble serializing and deserializing to that file.
I realize that there are other threads regarding this subject, but I cannot grasp or comprehend how to apply it to my code or the reasoning behind it.
So I have a bit of code that creates a file if it doesn't exist and writes JSON data to it which looks like:
public void SaveFile(Podcast podcast)
{
try
{
JsonSerializer serializer = new JsonSerializer();
if(!File.Exists(#"C: \Users\Kasper\Desktop\Projektuppgift\Projektuppgift - Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json"))
{
string json = JsonConvert.SerializeObject( new { Podcast = podcast });
StreamWriter sw = File.CreateText(#"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json");
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, json);
}
}
else
{
var filepath = #"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json";
var jsonData = File.ReadAllText(filepath);
var podcasts = JsonConvert.DeserializeObject<List<Podcast>>(jsonData) ?? new List<Podcast>();
podcasts.Add(podcast);
jsonData = JsonConvert.SerializeObject(new {PodcastList = podcasts });
File.WriteAllText(filepath, jsonData);
}
}
catch (Exception ex)
{
Console.WriteLine("IO Exception ", ex.Message);
}
}
What I can't get to work is to deserialize from this file and add an object to it. Is there an easier way to add more data to the JSON file or am I missing something?
The Podcast class looks like this:
public class Podcast
{
public string url { get; set; }
public string name { get; set; }
public int updateInterval { get; set; }
public string category { get; set; }
//public Category category = new Category();
public List<Episode> episodes { get; set; }
public Podcast(string url, string name, Category category, List<Episode> episodes, int updateInterval)
{
this.url = url;
this.name = name;
this.category = category.name;
this.episodes = episodes;
this.updateInterval = updateInterval;
}
public Podcast(Podcast p)
{
this.url = p.url;
this.name = p.name;
this.category = p.category;
this.episodes = p.episodes;
this.updateInterval = p.updateInterval;
}
}
There appear to be a couple of issues here:
You are checking for the existence of a different file than the one you are reading/writing. The former filename has extra spaces in it. The best way to avoid this problem is to use a variable to contain the filename rather than hardcoding it in three separate places.
You are inconsistent about the JSON format you are writing and reading:
When you first create the file (in the first branch), you are writing a JSON object that contains a property Podcast which then contains a single podcast.
When you attempt to read the JSON file, you are treating the entire JSON as a list of podcasts.
After tacking the new podcast onto the list, you are writing the JSON as a single object containing a PodcastList property, which then contains the list.
You need to use a consistent JSON format. I would recommend breaking your code into smaller methods to read and write the podcasts.json file like this so that it is easier to reason about:
public static List<Podcast> ReadPodcastsFromFile(string filepath)
{
if (!File.Exists(filepath)) return new List<Podcast>();
string json = File.ReadAllText(filepath);
return JsonConvert.DeserializeObject<List<Podcast>>(json);
}
public static void WritePodcastsToFile(List<Podcast> podcasts, string filepath)
{
string json = JsonConvert.SerializeObject(podcasts);
// This will overwrite the file if it exists, or create a new one if it doesn't
File.WriteAllText(filepath, json);
}
Then, you can simplify your SaveFile method down to this (I would be tempted to rename it to SavePodcast):
public void SaveFile(Podcast podcast)
{
var filepath = #"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json";
List<Podcast> podcasts = ReadPodcastsFromFile(filepath);
podcasts.Add(podcast);
WritePodcastsToFile(podcasts, filepath);
}
Notice I've also removed the exception handling from SaveFile. You should move that up to wherever SaveFile is called, so that you can take appropriate action at that point if an exception is thrown, e.g.:
try
{
SaveFile(podcast);
}
catch (Exception ex)
{
// Show a message to the user indicating that the file did not save
}
I'm just still learning c# but it might be that you deserialise into a list of podcasts and when you serialise you're serliasing into an object type.
I want to add a line break for each attribute for readability reasons when serializing xaml document.
The class I want to serialize looks like:
namespace XMLTest
{
[Serializable]
public class FHConfig
{
public string Name { get; set; } = "Configuration";
public string SettingA { get; set; } = "SettingA";
public string SettingB { get; set; } = "SettingB";
public string SettingC { get; set; } = "SettingC";
public FHConfig() { }
}
}
I use this code to save object as xaml file:
try
{
string path = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "config.xml");
using (TextWriter writer = File.CreateText(path))
{
FHConfig obj = new FHConfig();
XamlServices.Save(writer, obj);
}
}
catch (Exception exep) { MessageBox.Show("Saving UI parameters: " + exep.Message); }
It produces the file where each tag is in one text line:
<FHConfig Name="Configuration" SettingA="SettingA" SettingB="SettingB" SettingC="SettingC" xmlns="clr-namespace:XMLTest;assembly=XMLTest" />
But I want serializer to insert line breaks after each attribute. I know there is similar question How to add a line break when using XmlSerializer, but it addresses the same issue in xml documents.
XamlServices.Save() can use XamlWriter, but there is no clue on how to format output text in documentation.
You should use method XamlServices.Save Method (XmlWriter, Object) (msdn) and set property NewLineOnAttributes of XmlWriterSettings class to true:
try
{
var xmlWriterSettings = new XmlWriterSettings() { Indent = true, NewLineOnAttributes = true };
string path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "config.xml");
using (XmlWriter xmlWriter = XmlWriter.Create(path, xmlWriterSettings))
{
FHConfig obj = new FHConfig();
XamlServices.Save(xmlWriter, obj);
}
}
catch (Exception exep) { MessageBox.Show("Saving UI parameters: " + exep.Message); }
If you want to omit XML declaration from file "<?xml version="1.0" encoding="utf-8"?>" you should set OmitXmlDeclaration to true.
How can I deserialize the string based on what I have done in this method? Basically, what I have here is to pass the string through the network using serialization and deserialize the string in order to convey the message. But once I managed to receive the message, I have no idea if what I'm doing is correct. Here's the code:
string ConvertToString(FrogGame np, Frog1 pm, Frog2 pm2) //Serialization. the three parameters are the classes.
{
XmlSerializer sendSerializer = new XmlSerializer(typeof(FrogGame),new Type[]{typeof(Frog1),typeof(Frog2)});
StreamWriter myWriter = new StreamWriter(#"pad1.xml");
sendSerializer.Serialize(myWriter, np);
sendSerializer.Serialize(myWriter, pm);
sendSerializer.Serialize(myWriter, pm2);
return myWriter.ToString();
} //Overall, I serialize it into string
Once I pass the string through the network, I want to deserialize it in order the pass the message to the classes. How do I continue here onwards? How can I edit? The code:
void StringReceived(string str) //so str is myWriter.ToString()
{
XmlSerializer revSerializer = new XmlSerializer(typeof(FrogGame), new Type[] { typeof(Frog1), typeof(Frog2) });
FileStream myFileStream = new FileStream(#"pad1.xml", FileMode.Open);
FrogGame b = (FrogGame)revSerializer.Deserialize(myFileStream);
if (b is Frog1)
{
if (Network.IsServer())
{
pm = (Frog1)b;
pm.Position.Y = b.pm.Position.Y;
pm.Position.X = b.pm.Position.X;
}
else
{
System.Diagnostics.Debug.WriteLine("BAD Message: " + msg);
}
}
else if (b is Frog2)
{
if (Network.IsClient())
{
pm2 = (PaddleMessage2)b;
pm2.Position.Y = b.pm2.Position.Y;
pm2.Position.X = b.pm2.Position.X;
}
else
{
System.Diagnostics.Debug.WriteLine("BAD Message: " + msg);
}
}
}
I might misinterpret your problem, but I why don't you put all the thing you want to save in a class and do it like this (plus, if you use class, your data "transportation" and "management" will be much easier) :
SERIALIZATION
XmlSerializer serializer = new XmlSerializer(typeof(FrogGameData));
TextWriter textWriter = new StreamWriter("FrogGameSaveFile.xml");
serializer.Serialize(textWriter, _frogGameData);
textWriter.Close();
DESERIALIZATION
XmlSerializer deserializer = new XmlSerializer(typeof(FrogGameData));
TextReader textReader = new StreamReader("FrogGameSaveFile.xml");
_frogGameData = (FrogGameData)deserializer.Deserialize(textReader);
textReader.Close();
Note : The need-to-be-saved field should have property, because the tag in the XML will mimic the property name.
Additional Note : FrogGameData is not different than a normal class for automatic serialization like this. The XML will mimic your property order in the class for the one in the XML file.
But if you wanna need to rearrange the XML tag placement, you could do something like [XmlElement(Order = 1)],[XmlElement(Order = 2)], etc on top of your property to customize the order in XML file.
UPDATE
In case you need it, this is an example of your FrogGameData class :
public class FrogGameData
{
private Frog _frog1;
private Frog _frog2;
public Frog Frog1
{
get { return _frog1; }
set { _frog1 = value; }
}
public Frog Frog2
{
get { return _frog2; }
set { _frog2 = value; }
}
}
And the XML will pretty much like this :
<?xml version="1.0" encoding="utf-8"?>
<FrogGameData>
<Frog1>Something-depends-on-your-data</Frog1>
<Frog2>Something-depends-on-your-data</Frog2>
</FrogGameData>
But, if your class is (Note the XmlElement part) :
public class FrogGameData
{
private Frog _frog1;
private Frog _frog2;
[XmlElement(Order = 2)]
public Frog Frog1
{
get { return _frog1; }
set { _frog1 = value; }
}
[XmlElement(Order = 1)]
public Frog Frog2
{
get { return _frog2; }
set { _frog2 = value; }
}
}
Then, your XML will be :
<?xml version="1.0" encoding="utf-8"?>
<FrogGameData>
<Frog2>Something-depends-on-your-data</Frog2>
<Frog1>Something-depends-on-your-data</Frog1>
</FrogGameData>
I've been landed with a feed of XML data that I need to deserialise into objects in a Silverlight (v5) application. The data looks like:
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
I've created a class at the Silverlight side, and I'm trying to get this XML - which, you'll notice, is missing a declaration and a namespace - into objects.
StringReader sr = null;
string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
sr = new StringReader(data);
XmlSerializer xs = new XmlSerializer(typeof (AgentState));
AgentState agent = (AgentState) xs.Deserialize(sr);
.. but this throws an error an error in xml document (1,2), as it's missing the declaration. Even manually adding a dummy declaration gives further errors about missing namespaces.
I've found other questions about ignoring namespace/declarations in XML, but none of these seem to work in Silverlight.
Can anyone advise on the best way to get this XML deserialised into an object?
This seems to work:
public class AgentState
{
public string agentName { get; set; }
public string extension { get; set; }
public string currentlyIn { get; set; }
}
static void Main(string[] args)
{
var s = #"<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>";
XmlSerializer serializer = new XmlSerializer(typeof(AgentState));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(s));
var obj = serializer.Deserialize(ms);
}
I'm wondering what issue you have with appending the xml declaration to the string. This appears to work ok:
[System.Xml.Serialization.XmlRootAttribute("AgentState")]
public class AgentState
{
public string agentName {get; set;}
public int extension {get; set;}
public string currentlyIn {get; set;}
}
public void RunSerializer()
{
System.Xml.Serialization.XmlSerializer agent_serializer =
new System.Xml.Serialization.XmlSerializer(typeof(AgentState));
string agent_state_text = File.ReadAllText(#"C:\Temp\AgentState.xml");
Console.WriteLine(agent_state_text + Environment.NewLine);
string xml_agent_state = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + agent_state_text;
Console.WriteLine(xml_agent_state + Environment.NewLine);
AgentState agent_state = new AgentState();
using(StringReader tx_reader = new StringReader(xml_agent_state))
{
if (tx_reader != null)
{
agent_state = (AgentState)agent_serializer.Deserialize(tx_reader);
}
}
Console.WriteLine(agent_state.agentName);
Console.WriteLine(agent_state.extension);
Console.WriteLine(agent_state.currentlyIn);
}
Output:
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
<?xml version="1.0" encoding="UTF-8"?>
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
jbloggs
12345
TestStatus
I've managed to get it working using the following code - I'm not convinced it's the "right" way to do things, but it seems to work:
string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
var document = XDocument.Parse(data);
AgentState agent= (from c in document.Elements()
select new AgentState()
{
agentName = c.Element("agentName").Value,
extension = c.Element("extension").Value,
currentlyIn=c.Element("currentlyIn").Value
}).Single();
Thanks for the advice, it got me on the right track.
I've got an XML file that is created via my Windows form to save two text fields and 2 date time pickers.
I am wondering how to "load" (preferably by asking the user where the file is) this back into my form so that it can be edited and saved again.
public class Values
{
public string task1_name { get; set;}
public string task1_desc { get; set;}
public DateTime task1_date { get; set;}
public DateTime task1_time { get; set;}
}
Save Button on my form
void SavebuttonClick(object sender, EventArgs e)
{
DialogResult dialogResult = MessageBox.Show("Are you sure you want to save?",
"Save", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
Values v = new Values();
v.task1_name = this.task1_name.Text;
v.task1_desc = this.task1_desc.Text;
v.task1_date = this.task1_date.Value;
v.task1_time = this.task1_time.Value;
SaveValues(v);
}
}
Third Part
public void SaveValues(Values v)
{
XmlSerializer serializer = new XmlSerializer(typeof(Values));
using (TextWriter textWriter = new StreamWriter(#"E:\TheFile.xml"))
{
serializer.Serialize(textWriter, v);
}
}
You can do this:
public void SomeMethod()
{
Values v = LoadValues();
this.task1_name.Text = v.task1_name;
this.task1_desc.Text = v.task1_desc;
this.task1_date.Value = v.task1_date;
this.task1_time.Value = v.task1_time;
}
public Values LoadValues()
{
XmlSerializer serializer = new XmlSerializer(typeof(Values));
using (TextReader textReader = new StreamReader(#"E:\TheFile.xml"))
{
return (Values)serializer.Deserialize(textReader);
}
}
I recommend to have the serializer in one variable so it won't be created each time (it's expensive to construct a new XmlSerializer)
Hope it helps
You can deserialize the xml into an object and use that object to reload the fields..
For that first create the xsd for that xml using xsd.exe.. Then you can create the class file using the same exe and deserialize the xml into that object using XmlSerializer.
You can parse the xml and update the form with the parsed data. There is a file picker dialog in visual studio for the user to select a file.
XmlTextReader reader = new XmlTextReader ("books.xml");
while (reader.Read())
{
// code
}