I am learning XML Serialization and meet an issue, I have two claess
[System.Xml.Serialization.XmlInclude(typeof(SubClass))]
public class BaseClass
{
}
public class SubClass : BaseClass
{
}
I am trying to serialize a SubClass object into XML file, I use blow code
XmlSerializer xs = new XmlSerializer(typeof(Base));
xs.Serialize(fs, SubClassObject);
I noticed Serialization succeed, but the XML file is kind of like
<?xml version="1.0"?>
<BaseClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="SubClass">
...
</Employee>
If I use
XmlSerializer xs = new XmlSerializer(typeof(Base));
SubClassObject = xs.Deserialize(fs) as SubClass;
I noticed it will complain xsi:type is unknown attribute(I registered an event), although all information embedded in the XML was parsed successfully and members in SubClassObject was restored correctly.
Anyone has any idea why there is error in parsing xsi:type and anything I did wrong?
Thanks
Here is the program that I wrote
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace XmlIncludeExample
{
[XmlInclude(typeof(DerivedClass))]
public class BaseClass
{
public string ClassName = "Base Class";
}
public class DerivedClass : BaseClass
{
public string InheritedName = "Derived Class";
}
class Program
{
static void Main(string[] args)
{
string fileName = "Test.xml";
string fileFullPath = Path.Combine(Path.GetTempPath(), fileName);
try
{
DerivedClass dc = new DerivedClass();
using (FileStream fs = new FileStream(fileFullPath, FileMode.CreateNew))
{
XmlSerializer xs = new XmlSerializer(typeof(BaseClass));
xs.Serialize(fs, dc);
}
using (FileStream fs = new FileStream(fileFullPath, FileMode.Open))
{
XmlSerializer xs = new XmlSerializer(typeof(BaseClass));
DerivedClass dc2 = xs.Deserialize(fs) as DerivedClass;
}
}
finally
{
if (File.Exists(fileFullPath))
{
File.Delete(fileFullPath);
}
}
}
}
}
This produced the following xml
<?xml version="1.0" ?>
- <BaseClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="DerivedClass">
<ClassName>Base Class</ClassName>
<InheritedName>Derived Class</InheritedName>
</BaseClass>
And it worked
I got the same error.
I have not a great answer but this is what I have done:
using System;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
namespace HQ.Util.General
{
/// <summary>
/// Save by default as User Data Preferences
/// </summary>
public class XmlPersistence
{
// ******************************************************************
public static void Save<T>(T obj, string path = null) where T : class
{
if (path == null)
{
path = GetDefaultPath(typeof(T));
}
var serializer = new XmlSerializer(typeof(T));
using (TextWriter writer = new StreamWriter(path))
{
serializer.Serialize(writer, obj);
writer.Close();
}
}
// ******************************************************************
public static T Load<T>(string path = null,
Action<object, XmlNodeEventArgs> actionUnknownNode = null,
Action<object, XmlAttributeEventArgs> actionUnknownAttribute = null) where T : class
{
T obj = null;
if (path == null)
{
path = GetDefaultPath(typeof(T));
}
if (File.Exists(path))
{
var serializer = new XmlSerializer(typeof(T));
if (actionUnknownAttribute == null)
{
actionUnknownAttribute = UnknownAttribute;
}
if (actionUnknownNode == null)
{
actionUnknownNode = UnknownNode;
}
serializer.UnknownAttribute += new XmlAttributeEventHandler(actionUnknownAttribute);
serializer.UnknownNode += new XmlNodeEventHandler(actionUnknownNode);
using (var fs = new FileStream(path, FileMode.Open))
{
// Declares an object variable of the type to be deserialized.
// Uses the Deserialize method to restore the object's state
// with data from the XML document. */
obj = (T)serializer.Deserialize(fs);
}
}
return obj;
}
// ******************************************************************
private static string GetDefaultPath(Type typeOfObjectToSerialize)
{
return Path.Combine(AppInfo.AppDataFolder, typeOfObjectToSerialize.Name + ".xml");
}
// ******************************************************************
private static void UnknownAttribute(object sender, XmlAttributeEventArgs xmlAttributeEventArgs)
{
// Correction according to: https://stackoverflow.com/questions/42342875/xmlserializer-warns-about-unknown-nodes-attributes-when-deserializing-derived-ty/42407193#42407193
if (xmlAttributeEventArgs.Attr.Name == "xsi:type")
{
}
else
{
throw new XmlException("UnknownAttribute" + xmlAttributeEventArgs.ToString());
}
}
// ******************************************************************
private static void UnknownNode(object sender, XmlNodeEventArgs xmlNodeEventArgs)
{
// Correction according to: https://stackoverflow.com/questions/42342875/xmlserializer-warns-about-unknown-nodes-attributes-when-deserializing-derived-ty/42407193#42407193
if (xmlNodeEventArgs.Name == "xsi:type")
{
}
else
{
throw new XmlException("UnknownNode" + xmlNodeEventArgs.ToString());
}
}
// ******************************************************************
}
}
Related
i'm trying to load and save my data in my datagrid to an xml file using Singleton Design.
i have created
public class DataProvider
{
private static DataProvider singletonInstance = new DataProvider();
private ObservablePerson Person;
/// <summary>
/// Private constructor
/// </summary>
private DataProvider()
{
}
public static DataProvider GetInstance()
{
if (singletonInstance == null)
singletonInstance = new DataProvider();
return singletonInstance;
}
public bool SaveToXml(List<Person> PersonsList)
{
return false;
}
public List<Person> LoadFromX()
{
return new List<Person>() { new Person() { name= "jhon" } };
}
}
}
and this is the object i want to save
[Serializable]
public class ObservablePerson : ObservableObject
{
private string _name;
public string name
{
get
{
return _name;
}
set
{
_name= value;
NotifyPropertyChanged();}
and i also created a view model form from person.
what should i do to save those data in my datagrid in a xml file .
thanks.
Read an XML file using XmlTextReader and call Read method to read its node one by one until the end of file.
using System;
using System.Xml;
namespace ReadXml1 {
class Class1 {
static void Main(string[] args) {
// Create an isntance of XmlTextReader and call Read method to read the file
XmlTextReader textReader = new XmlTextReader("C:\\books.xml");
textReader.Read();
// If the node has value
while (textReader.Read()) {
// Move to fist element
textReader.MoveToElement();
Console.WriteLine("XmlTextReader Properties Test");
Console.WriteLine("===================");
// Read this element's properties and display them on console
Console.WriteLine("Name:" + textReader.Name);
Console.WriteLine("Base URI:" + textReader.BaseURI);
Console.WriteLine("Local Name:" + textReader.LocalName);
Console.WriteLine("Attribute Count:" + textReader.AttributeCount.ToString());
Console.WriteLine("Depth:" + textReader.Depth.ToString());
Console.WriteLine("Line Number:" + textReader.LineNumber.ToString());
Console.WriteLine("Node Type:" + textReader.NodeType.ToString());
Console.WriteLine("Attribute Count:" + textReader.Value.ToString());
}
}
}
}
and for creating and save to XML file:
using System;
using System.Xml;
namespace ReadingXML2 {
class Class1 {
static void Main(string[] args) {
// Create a new file in C:\\ dir
XmlTextWriter textWriter = new XmlTextWriter("C:\\myXmFile.xml", null);
// Opens the document
textWriter.WriteStartDocument();
// Write comments
textWriter.WriteComment("First Comment XmlTextWriter Sample Example");
textWriter.WriteComment("myXmlFile.xml in root dir");
// Write first element
textWriter.WriteStartElement("Student");
textWriter.WriteStartElement("r", "RECORD", "urn:record");
// Write next element
textWriter.WriteStartElement("Name", "");
textWriter.WriteString("Student");
textWriter.WriteEndElement();
// Write one more element
textWriter.WriteStartElement("Address", "");
textWriter.WriteString("Colony");
textWriter.WriteEndElement();
// WriteChars
char[] ch = new char[3];
ch[0] = 'a';
ch[1] = 'r';
ch[2] = 'c';
textWriter.WriteStartElement("Char");
textWriter.WriteChars(ch, 0, ch.Length);
textWriter.WriteEndElement();
// Ends the document.
textWriter.WriteEndDocument();
// close writer
textWriter.Close();
}
}
}
Let's use Xml and Xml.Serialization:
using System.Xml;
using System.Xml.Serialization;
On your singleton, implements
public static List<T> LoadFromXml<T>(string path, string fileName)
{
var xmlSerializer = new XmlSerializer(typeof(List<T>));
var xmlText = File.ReadAllText(Path.Combine(path, fileName));
return (List<T>)xmlSerializer.Deserialize(new StringReader(xmlText));
}
public static bool SaveToXml<T>(List<T> list, string path, string fileName)
{
var xmlText = Serialize<T>(list);
try
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
File.WriteAllText(Path.Combine(path, fileName), xmlText);
return true;
}
catch(Exception e)
{
return false;
}
}
private static string Serialize<T>(List<T> list)
{
if (list == null || !list.Any())
return string.Empty;
var xmlSerializer = new XmlSerializer(typeof(List<T>));
using (var stringWriter = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true }))
{
xmlSerializer.Serialize(xmlWriter, list);
return stringWriter.ToString();
}
}
}
Using generic T, we can use the same methods to save other types of records
To call:
var person1 = new Person()
{
name = "jhon",
};
var person2 = new Person()
{
name = "another jhon",
};
var personList = new List<Person>();
personList.Add(person1);
personList.Add(person2);
var pathXml = #"C:\testes\xml";
var fileName = "persons.xml";
var dataProvider = DataProvider.GetInstance();
var itsSaved = dataProvider.SaveToXml<Person>(personList, pathXml, fileName);
var persons = dataProvider.LoadFromXml<Person>(pathXml, fileName);
I found some discussion in same topic but still can't find the ultimate solution to a very simple task of serialize/deserialze an object in xml format.
The issue I run into is :
There is an error in XML document (2,2)
And the code to reproduce issue :
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Serialize_Click(object sender, EventArgs e)
{
testclass t = new testclass();
t.dummyInt = 10;
t.dummyString = "sssdf";
textBox1.Text = t.SerializeObject();
}
private void Deserialize_Click(object sender, EventArgs e)
{
try
{
object o = MySerializer.DeserializeObject<object>(textBox1.Text);
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message + Ex.InnerException, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
public class testclass
{
public int dummyInt;
public string dummyString;
public testclass() { }
}
public static class MySerializer
{
public static string SerializeObject<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
public static T DeserializeObject<T>(string data)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringReader sReader = new StringReader(data))
{
return (T)serializer.Deserialize(sReader);
}
}
}
So what is wrong here?
You are calling Deserialize with the wrong type.
This will build you a serializer to return an object from XML.
var o = MySerializer.DeserializeObject<object>(xml);
To have the above line not bark its xml input should look like:
<?xml version="1.0" encoding="utf-16"?>
<anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
If you want to return a testclass tell the serializer to do so:
var tc = MySerializer.DeserializeObject<testclass>(xml);
That will give you a testclass instance with your xml input (if I fix the errors in it)
I am trying to read from a xml file but it is not working eventhough I have been weating over it for two days, so any help would be greatly appreciated!
In Cookbook class:
public List<Recipe> readAll()
{
List<Recipe> newListRecipies = new List<Recipe>();
Recipe readRecipie = new Recipe();
TextReader reader = null;
try
{
var serializer = new XmlSerializer(typeof(Recipe));
reader = new StreamReader(path);
newListRecipies = BinarySerialization.ReadFromBinaryFile<List<Recipe>>(path);
reader.Close();
return newListRecipies;
}
catch (Exception e)
{
string error = $"An exception occured: " + e;
Log theLog = new Log();
theLog.LogMessage(error);
return newListRecipies;
}
}
In Recipe class:
public Recipe readOne(string name)
{
CookBook newCB = new CookBook();
List<Recipe> allRecipies = newCB.readAll();
foreach(Recipe oneRecipe in allRecipies)
{
if(oneRecipe.recipeName == name)
{
return oneRecipe;
}
}return newCB.defaultRecipie;
}
I am getting the default recipe as the result everytime. I can see the the recipies are saved correctly everytime but here the code anyways:
In Recipie class:
public void SaveRecipe(Recipe myRecepie)
{
CookBook theCookBook = new CookBook();
theCookBook.Save(myRecepie);
addFoodItem(myRecepie.recipeIngridients);
}
In CookBook class:
public void Save(Recipe newRecipie)
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof(Recipe));
writer = new StreamWriter(path, append: true);
serializer.Serialize(writer, newRecipie);
}
finally
{
if (writer != null)
writer.Close();
}
}
And the xml file (generated by the save function in the CookBook class)
<?xml version="1.0" encoding="utf-8"?>
<Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<recipeName>toast</recipeName>
<recipeType>snack</recipeType>
<recipeIngridients>
<string>bread</string>
<string>butter</string>
</recipeIngridients>
</Recipe><?xml version="1.0" encoding="utf-8"?>
<Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<recipeName>G&T</recipeName>
<recipeType>drink</recipeType>
<recipeIngridients>
<string>tonic</string>
<string>gin</string>
</recipeIngridients>
</Recipe><?xml version="1.0" encoding="utf-8"?>
<Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<recipeName>cake</recipeName>
<recipeType>snack</recipeType>
<recipeIngridients>
<string>butter</string>
<string>sugar</string>
</recipeIngridients>
</Recipe>
I believe the way you are deserializing the xml is incorrect with BinarySerialization.ReadFromBinaryFile....
Assuming your xml is correct I would do something like this.
// read file
List<Recipe> recipes;
using (var reader = new StreamReader("recipe.xml"))
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Recipe>),
new XmlRootAttribute("Recipe"));
recipes = (List<Recipe>)deserializer.Deserialize(reader);
}
These are the changes I made. It's best to load the previous recipes, add to the list, then re-write the XML from scratch.
public class Recipe
{
public string recipeName;
public string recipeType;
public List<string> recipeIngridients = new List<string>();
public Recipe readOne(string name)
{
CookBook newCB = new CookBook();
List<Recipe> allRecipies = newCB.readAll();
foreach(Recipe oneRecipe in allRecipies)
{
if(oneRecipe.recipeName == name)
{
return oneRecipe;
}
}
return newCB.defaultRecipe;
}
}
public class RecipeList
{
public List<Recipe> Recipes = new List<Recipe>();
}
public class CookBook
{
public Recipe defaultRecipe;
public string path;
public void Save(Recipe newRecipe)
{
TextWriter writer = null;
RecipeList recipeList = null;
try
{
// See if recipes exists
var serializer = new XmlSerializer(typeof(RecipeList));
if (File.Exists(path)) // Load the recipe list if it exists
{
using (var fileStream = File.OpenRead(path))
{
recipeList = (RecipeList)serializer.Deserialize(fileStream);
}
}
else
{
recipeList = new RecipeList();
}
// Add recipe to the list
recipeList.Recipes.Add(newRecipe);
writer = new StreamWriter(path, append: false);
serializer.Serialize(writer, recipeList);
}
finally
{
if (writer != null)
writer.Close();
}
}
public List<Recipe> readAll()
{
RecipeList temp = null;
var serializer = new XmlSerializer(typeof(RecipeList));
try
{
using (var fileStream = File.OpenRead(path))
{
temp = (RecipeList)serializer.Deserialize(fileStream);
}
return temp.Recipes;
}
catch (Exception e)
{
string error = #"An exception occured: " + e;
//Log theLog = new Log();
//theLog.LogMessage(error);
return new List<Recipe>();
}
}
}
I've just wanted to use the XML-Serializer of the .NET framework(version 2.0).
I created to methods to serialize and deserialize my settings:
public static void Save(string filename)
{
var settings = Settings.Instance;
if (File.Exists(filename))
File.Delete(filename);
using (var stream = File.OpenWrite(filename))
{
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
serializer.Serialize(stream, settings);
}
}
The Save-methods works really fine and as a result I get the following xml document:
<?xml version="1.0"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EnableHooking>true</EnableHooking>
<IncludePressedKeys>false</IncludePressedKeys>
<EnableFastScroll>false</EnableFastScroll>
<FastScrollingHotKeys>
<VirtualKeys>Control</VirtualKeys>
<VirtualKeys>Alt</VirtualKeys>
</FastScrollingHotKeys>
<ScrollSpeed>2</ScrollSpeed>
<FastScrollSpeed>10</FastScrollSpeed>
</Settings>
If I try to deserialize this document I get an exception XmlException which tells me that the root element is missing. I've tried to set the XmlRootAttribute, tried to check the filenames and stream position. Everything is ok. Now I finally tried to read load the file through the XmlDocument class which works perfectly. Now I really don't know what happens. So you may take a look at the Load-method:
public static void Load(string filename)
{
if (!File.Exists(filename))
throw new ArgumentException("File not found.", "filename", new FileNotFoundException());
//works
var doc = new XmlDocument();
doc.Load(XmlReader.Create(File.OpenRead(filename)));
Console.WriteLine(doc.DocumentElement.FirstChild);
using (var stream = File.OpenRead(filename))
{
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
_instance = serializer.Deserialize(stream) as Settings;
}
}
Hopefully anyone got an idea.
When I use the following code (in which I added a simple version of the Settings class) on OSX with Xamarin Studio, I get no errors.
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Test
{
class MainClass
{
private static Settings _instance;
public static void Main (string[] args)
{
Load ("Settings.xml");
}
public static void Load(string filename)
{
if (!File.Exists(filename))
throw new ArgumentException("File not found.", "filename", new FileNotFoundException());
//works
var doc = new XmlDocument();
doc.Load(XmlReader.Create(File.OpenRead(filename)));
Console.WriteLine(doc.DocumentElement.FirstChild);
using (var stream = File.OpenRead(filename))
{
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
_instance = serializer.Deserialize(stream) as Settings;
}
}
}
public class Settings
{
public bool EnableHooking {
get;
set;
}
public bool IncludePressedKeys {
get;
set;
}
}
}
When I check the value of _instance, the properties are set to the right values. I stripped the XML after the second property. The problem might be in more "complex" XmlElement "FastScrollingHotKeys". Could you post your Settings class, please?
I get an object of a class with some properties by calling its own static function for an instance. If there is a XML file the object tries to load it and add its values to the instance itself. Then it will save the XML again in case there are missing options in the XML file.
I created a small console app:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml;
namespace Test
{
public class Program
{
static void Main(string[] args)
{
TaskServerSettings s = TaskServerSettings.LoadNew();
}
}
public class TaskServerSettings : IEqualityComparer
{
#region SETTINGS PROPERTIES
public bool Enabled { get; set; }
public int CheckInterval { get; set; }
#endregion
#region CONSTRUCTORS AND METHODS
public TaskServerSettings()
{
this.init();
}
public TaskServerSettings(string settingsFile)
{
this.init();
if (settingsFile != null)
{
if (File.Exists(settingsFile))
{
this.Load(settingsFile);
}
this.Save(settingsFile);
}
}
private void init()
{
this.Enabled = true;
this.CheckInterval = 5000;
}
public void Absorb(TaskServerSettings newSettings)
{
this.Enabled = newSettings.Enabled;
this.CheckInterval = newSettings.CheckInterval;
}
public static TaskServerSettings LoadNew(string settingsFile = null)
{
if (settingsFile == null)
{
settingsFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.TrimEnd('\\')) + #"\TaskServerSettings.xml";
}
return new TaskServerSettings(settingsFile);
}
public bool Load(string settingsFile = null)
{
if (settingsFile == null)
{
settingsFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.TrimEnd('\\')) + #"\TaskServerSettings.xml";
}
if (!File.Exists(settingsFile))
{
throw new FileNotFoundException("Could not find \"" + settingsFile + "\" to load settings.");
}
bool result = false;
using (FileStream fs = new FileStream(settingsFile, FileMode.Open))
{
XmlSerializer xs = new XmlSerializer(this.GetType());
if (!xs.CanDeserialize(XmlReader.Create(fs)))
{
throw new XmlException("\"" + settingsFile + "\" does not have a valid TaskServerSettings XML structure.");
}
//try
//{ // +- InvalidOperationException - Error in XML document (0,0).
// v The root element is missing.
this.Absorb(xs.Deserialize(fs) as TaskServerSettings);
result = true;
//}
//catch { }
}
return result;
}
public bool Save(string settingsFile = null)
{
if (settingsFile == null)
{
settingsFile = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.TrimEnd('\\')) + #"\TaskServerSettings.xml";
}
bool result = false;
using (FileStream fs = new FileStream(settingsFile, FileMode.Create))
{
XmlSerializer xs = new XmlSerializer(this.GetType());
try
{
xs.Serialize(fs, this);
result = true;
}
catch { }
}
return result;
}
#endregion
public bool Equals(TaskServerSettings settingsToCompare)
{
if (this.Enabled != settingsToCompare.Enabled ||
this.CheckInterval != settingsToCompare.CheckInterval)
{
return false;
}
return true;
}
bool IEqualityComparer.Equals(object x, object y)
{
return x.Equals(y);
}
int IEqualityComparer.GetHashCode(object obj)
{
throw new NotSupportedException();
}
}
}
Writing the object with its default property values in the first run works pretty good.
The XML file looks like this then:
<?xml version="1.0"?>
<TaskServerSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Enabled>true</Enabled>
<CheckInterval>5000</CheckInterval>
</TaskServerSettings>
However, deserializing the same file on the second run causes the error when it tries to load the file on
xs.Deserialize(fs) as TaskServerSettings.
InvalidOperationException - Error in XML document (0,0).
The root element is missing.
I already tried to avoid the static method and tried new as well as I already tried to remove the IEqualityComparer parent + the last three methods. Without success.
I wonder, whats the cause of this error?
When you execute this statement:
if (!xs.CanDeserialize(XmlReader.Create(fs)))
it starts reading the stream. So when you call Deserialize later, the stream is not at the start, so the deserialization fails. You need to rewind the stream by setting fs.Position = 0