Object xml serialization / deserialization - c#

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)

Related

XML deserialization with extra types throws error

I do not know the type of the XML before I deserialize it. So I'm using the overload of XmlSerializer with extraTypes. It does work for the first type, but it does not work for all the extraTypes. I'm getting the following errorMsg "System.InvalidOperationException: There is an error in XML document (1, 40). ---> System.InvalidOperationException: <Bike xmlns=''> was not expected.". Here's a dummy code of what I'm trying to do.
using System;
using System.IO;
using System.Xml.Serialization;
public class Program
{
private const string CarXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Car xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"></Car>";
private const string BikeXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><Bike xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"></Bike>";
public static void Main(string[] args)
{
var xmlSerializer = new XmlSerializer(typeof(Car), new[] { typeof(Bike) });
using (var stringReader = new StringReader(BikeXml))
{
try
{
var vehicle = (Vehicle)xmlSerializer.Deserialize(stringReader);
Console.WriteLine(vehicle.PrintMe());
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
public abstract class Vehicle
{
public abstract string PrintMe();
}
public class Car : Vehicle
{
public override string PrintMe()
{
return "beep beep I'm a car!";
}
}
public class Bike : Vehicle
{
public override string PrintMe()
{
return "bing bing I'm a bike!";
}
}
Apparently the extraTypes are not meant to be used for my case. See msdn documentation.
I solved it by getting the XmlRoot.
public static void Main(string[] args)
{
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(BikeXml);
var rootName = xmlDocument.DocumentElement.Name;
var rootType = Type.GetType(rootName);
var vehicle = rootType != null ? (Vehicle) Activator.CreateInstance(rootType) : null;
Console.WriteLine(vehicle != null ? vehicle.PrintMe() : "Error");
}

issue reading from xml file c#

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>();
}
}
}

Serialize List<T> to XML, and reverse the XML to List<T>

Does anyone know how I (or if it's possible to) reverse the XML I'm creating below
[Serializable()]
public class CustomDictionary
{
public string Key { get; set; }
public string Value { get; set; }
}
public class OtherClass
{
protected void BtnSaveClick(object sender, EventArgs e)
{
var analysisList = new List<CustomDictionary>();
// Here i fill the analysisList with some data
// ...
// This renders the xml posted below
string myXML = Serialize(analysisList).ToString();
xmlLiteral.Text = myXML;
}
public static StringWriter Serialize(object o)
{
var xs = new XmlSerializer(o.GetType());
var xml = new StringWriter();
xs.Serialize(xml, o);
return xml;
}
}
The xml rendered
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfCustomDictionary xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CustomDictionary>
<Key>Gender</Key>
<Value>0</Value>
</CustomDictionary>
<CustomDictionary>
<Key>Height</Key>
<Value>4</Value>
</CustomDictionary>
<CustomDictionary>
<Key>Age</Key>
<Value>2</Value>
</CustomDictionary>
</ArrayOfCustomDictionary>
Now, after a few hours of Googling and trying I'm stuck (most likely my brain have some vacation already). Can anyone help me how to reverse this xml back to a List?
Thanks
Just deserialize it:
public static T Deserialize<T>(string xml) {
var xs = new XmlSerializer(typeof(T));
return (T)xs.Deserialize(new StringReader(xml));
}
Use it like this:
var deserializedDictionaries = Deserialize<List<CustomDictionary>>(myXML);
XmlSerializer.Deserialize

Custom XML-element name for base class field in serialization

How can I change XML-element name for field inherited from base class while doing serialization?
For example I have next base class:
public class One
{
public int OneField;
}
Serialization code:
static void Main()
{
One test = new One { OneField = 1 };
var serializer = new XmlSerializer(typeof (One));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
I get what I need:
<?xml version="1.0" encoding="utf-8"?>
<One xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
</One>
Now I have created new class inherited from A with additional field and custom XML element name for it:
public class Two : One
{
[XmlElement("SecondField")]
public int TwoField;
}
Serialization code:
static void Main()
{
Two test = new Two { OneField = 1, TwoField = 2 };
var serializer = new XmlSerializer(typeof (Two));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
As a result I get next output:
<?xml version="1.0" encoding="utf-8"?>
<Two xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
<SecondField>2</SecondField>
</Two>
The problem is that I want to change OneField in this output to FirstField without touching base class code (because I will use it too and the names must be original). How can I accomplish this?
Thank you.
Try this:
public class Two : One
{
private static XmlAttributeOverrides xmlOverrides;
public static XmlAttributeOverrides XmlOverrides
{
get
{
if (xmlOverrides == null)
{
xmlOverrides = new XmlAttributeOverrides();
var attr = new XmlAttributes();
attr.XmlElements.Add(new XmlElementAttribute("FirstField"));
xmlOverrides.Add(typeof(One), "OneField", attr);
}
return xmlOverrides;
}
}
[XmlElement("SecondField")]
public string TwoField;
}
And your serializer init is a lot easier:
var xmls = new System.Xml.Serialization.XmlSerializer(typeof(Two), Two.XmlOverrides);
Here's a workaround: Override the fields in the subclass and mark the overriden field with whatever name you need. For example,
class One
{
public int OneField { get; set; }
}
class Two : One
{
[XmlElement("FirstField")]
public new int OneField
{
get { return base.OneField; }
set { base.OneField = value; }
}
}

Unknown attribute xsi:type in XmlSerializer

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());
}
}
// ******************************************************************
}
}

Categories