XmlSerializing only the base object - c#

Currently I am trying to write a set of two classes: one (BackgroundManagerSettings) which will hold all the variables that I wish to save when the program is closed, and another (BackgroundManager) which contains methods and variables that will only be needed during the runtime.
Code:
public class BackgroundManager : BackgroundManagerSettings
{
//Example
private string _LastName;
public string LastName
{
get
{
return this._LastName;
}
set
{
this._LastName = value;
}
}
public BackgroundManager()
{
this.LastName = "Smith";
}
public static BackgroundManager Load(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManager));
LoopAgain:
try
{
using (StreamReader Reader = new StreamReader(filename))
{
return Serializer.Deserialize(Reader) as BackgroundManager;
}
}
catch (FileNotFoundException)
{
using (StreamWriter Writer = new StreamWriter(filename))
{
Serializer.Serialize(Writer, new BackgroundManager() as BackgroundManagerSettings);
Writer.Close();
}
goto LoopAgain;
}
catch (InvalidOperationException)
{
File.Delete(filename);
goto LoopAgain;
}
}
public void Save(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManager));
using (StreamWriter Writer = new StreamWriter(filename))
{
Serializer.Serialize(Writer, this as BackgroundManagerSettings);
Writer.Close();
}
}
}
public abstract class BackgroundManagerSettings
{
//Example
private string _FirstName;
[XmlElement("FirstName")]
public string FirstName
{
get
{
return this._FirstName;
}
set
{
this._FirstName = value;
}
}
public BackgroundManagerSettings()
{
this.FirstName = "Joe";
}
}
Usage:
BackgroundManager Manager = BackgroundManager.Load("temp.Xml");
Manager.Save("temp.Xml");
Output:
<?xml version="1.0" encoding="UTF-8"?>
<BackgroundManager xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FirstName>Joe</FirstName>
<LastName>Smith</LastName>
</BackgroundManager>
My current problem is that when I use the Save() Method it serializes the fields of BackgroundManager as well as the fields of BackgroundManagerSettings. How do I make it so it only serializes the fields of BackgroundManagerSettings? Any help or links to point me in the right direction would be much appreciated.

Mark all the public members of the base class that you don't want to be serialized with XmlIgnore attribute.

I would do it like this:
[DataContract]
public class BackgroundManager : BackgroundManagerSettings
{
[DataMember]
public int PropertyToSerialize { get; set; }
}
Add the DataMember decoration to all of the properties that you want included.

After some more research I found a method that works how I want it to thanks for the advice.
public static BackgroundManager Load(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManager));
LoopAgain:
try
{
using (StreamReader Reader = new StreamReader(filename))
{
return Serializer.Deserialize(Reader) as BackgroundManager;
}
}
catch (FileNotFoundException)
{
XmlSerializer BaseSerializer = new XmlSerializer(typeof(BackgroundManagerSettings));
using (StreamWriter Writer = new StreamWriter(filename))
{
BaseSerializer.Serialize(Writer, new BackgroundManager().ToBase());
Writer.Close();
}
goto LoopAgain;
}
catch (InvalidOperationException)
{
File.Delete(filename);
goto LoopAgain;
}
}
public void Save(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManagerSettings));
using (StreamWriter Writer = new StreamWriter(filename))
{
Serializer.Serialize(Writer, this.ToBase());
Writer.Close();
}
}
private dynamic ToBase()
{
var Temp = Activator.CreateInstance(typeof(BackgroundManagerSettings));
FieldInfo[] Fields = Temp.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo x in Fields)
{
x.SetValue(Temp, x.GetValue(this));
}
return Temp;
}

Related

Deserialize xml into a list

I have a normal list based in model like:
Model:
public class ProjectHistoryModel
{
public int JobNumber { get; set; }
public int DesignKey { get; set; }
public string ProjectName { get; set; }
}
In class I have a list of this model like:
public List<ProjectHistoryModel> ProjectHistoryModel = new List<ProjectHistoryModel>();
Then I save that list into xml file as:
Serialize list:
public static string SerializeObject<T>(this T value)
{
if (value == null)
{
return string.Empty;
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = XmlWriter.Create(stringWriter))
{
xmlserializer.Serialize(writer, value);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
So I save list just sending it to that method as:
var historyXml = ProjectHistoryModel.SerializeObject();
XML.HistoryProjects = historyXml;
XML.SaveXML();
Now my question is: How can I deserialize this xml and convert to a list again?
I try it something like this but I get stuck:
public static List<string> Load()
{
var xmlList = XML.HistoryProjects;
using (var stream = System.IO.File.OpenRead(FileName))
{
var serializer = new XmlSerializer(xmlList));
return serializer.Deserialize(stream) as [xmlList];
}
}
Regards
You just need to do the same thing in reverse, using a StringReader instead of a writer.
public static T DeserializeObject<T>(this string source)
{
if (string.IsNullOrEmpty(source))
{
return default(T);
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringReader = new StringReader(source);
using (var reader = XmlReader.Create(stringReader))
{
var result = xmlserializer.Deserialize(reader);
return (T)result;
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
Then call it with:
var input = new List<ProjectHistoryModel>();
var serialized = input.SerializeObject();
var output = serialized.DeserializeObject<List<ProjectHistoryModel>>();
Here is a link to a working example on DotNetFiddle.

.Net Xml whitespace deserialization

Just tried to serialize and deserialize several whitespace with additional xml attribute to preserve them as described in msdn (see remarks). Here is simple C# example.
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace XmlTextTest
{
[Serializable]
public class WhitespaceTest
{
[XmlAttribute("space", Namespace = "http://www.w3.org/XML/1998/namespace")]
public string Space { get; set; } = "preserve";
[XmlText]
public string Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var testValue = " ";
var test = new WhitespaceTest { Value = testValue };
var serializer = new XmlSerializer(typeof(WhitespaceTest));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, test);
stream.Seek(0, SeekOrigin.Begin);
Console.WriteLine(new StreamReader(stream).ReadToEnd());
stream.Seek(0, SeekOrigin.Begin);
var deserialized = serializer.Deserialize(stream) as WhitespaceTest;
if (deserialized.Value != testValue)
throw new Exception();
}
}
}
}
Result xml
<WhitespaceTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:space="preserve"> </WhitespaceTest>
May be it's my mistake but I expect that whitespaces will be deserialized as is (according to standard), not "null". How to mark original Type property to prevent replacing whitespaces with "null" into XmlText region.
Modified example to use IgnoreWhiteSpace property:
var str = "<WhitespaceTest xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xml:space='preserve'> </WhitespaceTest>";
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = false;
var reader = XmlReader.Create(new StringReader(str), settings);
var deserialized2 = serializer.Deserialize(reader) as WhitespaceTest;
if (deserialized2.Value != testValue)
throw new Exception();
Still no effect.
Updated:
Created implementation of IXmlSerializable and added "xml:space=preserve" attribute manually. See example:
public class Sample : IXmlSerializable
{
#region Properties
[XmlText]
public string Text { get; set; }
#endregion
#region IXmlSerializable members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
ReadXml(this, reader);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
WriteXml(this, writer);
}
#endregion IXmlSerializable members
internal static void ReadXml(ILocalizedMappable serializable, System.Xml.XmlReader reader)
{
var node = new XmlDocument().ReadNode(reader);
if (node != null)
{
serializable.Text = node.InnerText;
}
}
internal static void WriteXml(ILocalizedMappable serializable, System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("xml", "space", string.Empty, "preserve");
writer.WriteString(serializable.Text);
}
}

Properties not getting serialized

I generate a runtime class using reflection.emit, the generation of the class seems to be fine as it shows in ILSpy:
using indice.Edi.Serialization;
using IQoneEDIParser.Formats;
using System;
using System.Xml.Serialization;
[XmlRoot("CONTRL"), XmlType("CONTRL")]
[Serializable]
public class CONTRL : FormatBase
{
private string _syntaxkennung;
private int _sintaxversion;
private string _absenderid;
private string _absendercodeunb;
private string _empfängerid;
private string _empfängercodeunb;
private string _dokumentdatum;
private string _dokumentzeit;
private string _datenaustauschreferenz;
[EdiValue("X(4)", "UNB/0"), XmlElement("Syntaxkennung", typeof(string))]
public string Syntaxkennung
{
get
{
return this._syntaxkennung;
}
set
{
this._syntaxkennung = value;
}
}
[EdiValue("9(1)", "UNB/0/1"), XmlElement("Sintaxversion", typeof(int))]
public int Sintaxversion
{
get
{
return this._sintaxversion;
}
set
{
this._sintaxversion = value;
}
}
[EdiValue("X(35)", "UNB/1/0"), XmlElement("AbsenderID", typeof(string))]
public string AbsenderID
{
get
{
return this._absenderid;
}
set
{
this._absenderid = value;
}
}
[EdiValue("X(4)", "UNB/1/1"), XmlElement("AbsenderCodeUNB", typeof(string))]
public string AbsenderCodeUNB
{
get
{
return this._absendercodeunb;
}
set
{
this._absendercodeunb = value;
}
}
[EdiValue("X(35)", "UNB/2/0"), XmlElement("EmpfängerID", typeof(string))]
public string EmpfängerID
{
get
{
return this._empfängerid;
}
set
{
this._empfängerid = value;
}
}
[EdiValue("X(4)", "UNB/2/1"), XmlElement("EmpfängerCodeUNB", typeof(string))]
public string EmpfängerCodeUNB
{
get
{
return this._empfängercodeunb;
}
set
{
this._empfängercodeunb = value;
}
}
[EdiValue("X(6)", "UNB/3/0"), XmlElement("Dokumentdatum", typeof(string))]
public string Dokumentdatum
{
get
{
return this._dokumentdatum;
}
set
{
this._dokumentdatum = value;
}
}
[EdiValue("X(4)", "UNB/3/1"), XmlElement("Dokumentzeit", typeof(string))]
public string Dokumentzeit
{
get
{
return this._dokumentzeit;
}
set
{
this._dokumentzeit = value;
}
}
[EdiValue("X(14)", "UNB/4/0"), XmlElement("Datenaustauschreferenz", typeof(string))]
public string Datenaustauschreferenz
{
get
{
return this._datenaustauschreferenz;
}
set
{
this._datenaustauschreferenz = value;
}
}
}
For any unknown reason, the only property getting serialized to Xml is the Syntaxversion (the only one which is Type Integer)
Here the serialization method:
public static String SerializeToXml(FormatBase toSerialize, Type inType)
{
XmlWriterSettings xmlSettings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Indent = true,
OmitXmlDeclaration = true,
NewLineOnAttributes = true,
CloseOutput = true
};
using (StringWriter stringWriter = new StringWriter())
{
XmlSerializer serializer2 = new XmlSerializer(inType);
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, xmlSettings))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer2.Serialize(xmlWriter, toSerialize, ns);
return stringWriter.ToString();
}
}
}
Any Ideas about why the rest of properties are not getting serialised? Thanks in advance!
EDIT_1:
After further tests I copy-paste the generated class and created it as a "normal" type in my project, try again the Serialization method, and work as expected (See EDIT_2)
Can it be a Reflection.Emit problem and/or my runtime assembly?
EDIT_2:
Expected result from the Xml Serialization:
<CONTRL>
<Syntaxkennung>UNOC</Syntaxkennung>
<Sintaxversion>3</Sintaxversion>
<AbsenderID>9904325000003</AbsenderID>
<AbsenderCodeUNB>500</AbsenderCodeUNB>
<EmpfängerID>9900080000007</EmpfängerID>
<EmpfängerCodeUNB>500</EmpfängerCodeUNB>
<Dokumentdatum>161007</Dokumentdatum>
<Dokumentzeit>1723</Dokumentzeit>
</CONTRL>
Received:
<CONTRL>
<Sintaxversion>3</Sintaxversion>
</CONTRL>
I end up implementing IXmlSerializable in the FormatBase (Base class)
public void WriteXml(XmlWriter writer)
{
foreach (PropertyInfo property in GetType().GetProperties())
{
writer.WriteElementString(property.Name, property.GetValue(this)?.ToString());
}
}
And working as expected.

Avoid duplicated code in XML serializable classes

I have two Serializable classes with very similar code. Actually, except for the part where specific constructor is called, serialization code is identical.
Is there a way to create a common class to contain the common parts, so that specific classes (subclasses?) can implement only the constructor part? I can think of generics, factory pattern, but could not figure out how to do it.
// Fictitious classes
[Serializable]
public class FlightParameters {
public double MaxHeight { get; set; }
pulbic double MaxSpeedKmPerHour { get; set; }
public static FlightParameters Load(String fname) {
FlightParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(FlightParameters));
try {
result = (FlightParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new FlightParameters() {
MaxHeight = 30000;
MaxSpeedKmPerHour = 1500;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
[Serializable]
public class SailingParameters {
public double MaxDepth { get; set; }
pulbic double MaxSpeedKnots { get; set; }
public static SailingParameters Load(String fname) {
SailingParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(SailingParameters));
try {
result = (SailingParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new SailingParameters() {
MaxDepth = 13000;
MaxSpeedKnots = 15;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
Usage:
FlightParameters _fparam = FlightParameters.Load(somePath);
SailingParameters _sparam = SailingParameters.Load(someOtherPath);
The easiest way I can see to do that would be something like:
static class XmlUtils {
public static T Load<T>(string filename, Func<T> onMissing = null)
where T : class, new()
{
using (var fs = File.OpenRead(filename)) {
var serializer = new XmlSerializer(typeof(T));
try {
return (T)serializer.Deserialize(fs);
} catch (InvalidOperationException) { // catch "file not found"
return onMissing == null ? new T() : onMissing();
}
}
}
}
allowing something like;
public static SailingParameters Load(string filename) {
return XmlUtils.Load<SailingParameters>(filename, () => new SailingParameters {
MaxDepth = 13000;
MaxSpeedKnots = 15;
});
}

Remove namespace from DataContract doesn't work

I have to two simple serialize/desirialize methods,
Mapping:
[System.Runtime.Serialization.DataContract(Namespace = "", Name = "PARAMS")]
public sealed class CourseListRequest {
[DataMember(Name = "STUDENTID")]
public int StudentId { get; set; }
[DataMember(Name = "YEAR")]
public string Year { get; set; }
[DataMember(Name = "REQUESTTYPE")]
public int RequestType { get; set; }
}
public static string Serialize<T>(this T value) {
if (value == null) throw new ArgumentNullException("value");
try {
var dcs = new DataContractSerializer(typeof (T));
string xml;
using (var ms = new MemoryStream()) {
dcs.WriteObject(ms, value);
xml = Encoding.UTF8.GetString(ms.ToArray());
}
return xml;
}
catch (Exception e) {
throw;
}
}
public static T Deserialize<T>(this string xml) where T : class {
if (string.IsNullOrEmpty(xml)) {
return default(T);
}
try {
var dcs = new DataContractSerializer(typeof (T));
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml))) {
ms.Position = 0;
return dcs.ReadObject(ms) as T;
}
}
catch (Exception e) {
throw;
}
}
result:
<PARAMS xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><REQUESTTYPE>36</REQUESTTYPE><STUDENTID>0</STUDENTID><YEAR>תשע</YEAR></PARAMS>
How to remove xmlns:i="http://www.w3.org/2001/XMLSchema-instance" ?? On serializing
Switch to using XmlSerializer
System.Xml.Serialization.XmlSerializer
This will generate plain XML with no namespaces

Categories

Resources