Property: Get: Set: Serialize to XML - c#

I have a class defined that auto increments a property in its get method. I am trying to serialize this object to an XML and the auto-incremented property is not being printed. Any help is appreciated.
public class Program
{
public static void Main()
{
MyClass _myClass = new MyClass();
string transactionXML = string.Empty;
Console.WriteLine("Current ID: " + _myClass.ID);
System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(MyClass));
System.IO.StringWriter _sw = new System.IO.StringWriter();
System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(_sw);
xmlSerializer.Serialize(writer, _myClass);
transactionXML = _sw.ToString();
Console.WriteLine("XML:\n" + transactionXML);
}
[Serializable]
public class MyClass
{
long last_id = 0;
public string ID{get { return System.Threading.Interlocked.Increment(ref last_id ).ToString("D6"); }}
}
}
When I try to run this, it runs with no errors, but does not print the ID in the XML.

you need to extend you "MyClass" ID with a setter like this:
[Serializable]
public class MyClass
{
long last_id = 0;
public string ID { get { return System.Threading.Interlocked.Increment(ref last_id).ToString("D6"); } set { } }
}

Limitation of XMLSerializer - Properties without a setter can't be serialized.
But you can use DataContractSerializer to serialize private setter properties -
[DataMember]
public string Id
{
get
{
return Guid.NewGuid().ToString();
}
private set {}
}

Related

How to create an object after deserialization in c# in xml

I am trying to create an object after deserialization however I do not want to serialize that object.
Here is an example:
public class Visit : INotifyPropertyChanged
{
private int id;
private int serialNumber;
private DateTime dateOfVisit;
private VisitTime visitTime;
private VisitType visitType;
private int price;
private VisitStatus visitStatus;
private PaymentMethod paymentMethod;
private String cause;
private String recommendation;
private Prescription prescription;
private Patient patient;
[NonSerialized]
private Doctor doctor;
private Room room;
// more code below...
}
I do not want to serialize the doctor, I have the doctors in completely different xml file. I am serializing doctorID property fine. After deserialization of the properties that I have serialized I want to create a new instance of a Doctor class and assign it to doctor field in Visit class. How can I do that correctly?
Here is the xml that I am serializing:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfVisit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Visit>
<Id>1</Id>
<SerialNumber>1</SerialNumber>
<VisitTime>
<StartTime>2021-05-02T09:00:00</StartTime>
<EndTime>2021-05-02T10:00:00</EndTime>
</VisitTime>
<DateOfVisitString>1. Jan 00:00 AM</DateOfVisitString>
<DateOfVisit>0001-01-01T00:00:00</DateOfVisit>
<VisitType>examination</VisitType>
<VisitTypeString>Pregled</VisitTypeString>
<Price>10</Price>
<PriceString>10$</PriceString>
<VisitStatus>forthcoming</VisitStatus>
<VisitStatusString>Predstojeći</VisitStatusString>
<DoctorName>Janko Radovic</DoctorName>
<DoctorId>12123131</DoctorId>
<PaymentMethod>digitalWallet</PaymentMethod>
</Visit>
</ArrayOfVisit>
I tried using onDeserializing() and onDeserialized() methods but I get a null pointer exception when data is binded to View with DoctorName property. It just says doctor is null.
[System.Xml.Serialization.XmlIgnore]
public Doctor Doctor
{
get
{
return doctor;
}
set
{
doctor = value;
OnPropertyChanged("Doctor");
OnPropertyChanged("DoctorName");
OnPropertyChanged("DoctorId");
}
}
public String DoctorName
{
get
{
return doctor.Name + " " + doctor.Surname;
}
set
{
}
}
public String DoctorId
{
get
{
return doctor.Id;
}
set
{
}
}
[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
DoctorService doctorService = new DoctorService();
doctor = doctorService.getDoctorById(DoctorId);
}
Here is how I serialize and deserialize:
public void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
try
{
var temp = AppDomain.CurrentDomain.BaseDirectory;
temp = Path.Combine(temp.Substring(0, temp.Length - 10), "Storage\\");
fileName = Path.Combine(temp, fileName);
StreamWriter w = new StreamWriter(fileName);
serializer.Serialize(w, serializableObject);
w.Close();
}
catch (Exception e)
{
//Log exception here
}
}
public T DeSerializeObject<T>(string fileName)
{
var temp = AppDomain.CurrentDomain.BaseDirectory;
temp = Path.Combine(temp.Substring(0, temp.Length - 10), "Storage\\");
fileName = Path.Combine(temp, fileName);
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try
{
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
StreamReader r = new StreamReader(fileName);
objectOut = (T)serializer.Deserialize(r);
r.Close();
}
catch (Exception e)
{
//Log exception here
}
return objectOut;
}
Any suggestion how I can do this? Just serialize the id and then by using that id to create a Doctor object in Visit class? Thank you!
So according to mm8 who suggested a thing that worked out this is how it should be done.
XMLSerializer works with get and set properties of a model class. So when serializing a particular class object the way serialization works is that it uses get method of that property to serialize. To deserialize it uses set method to again set the fields of that class object that was serialized.
If you do not want to serialize a particular field of a class you should just set [System.Xml.Serialization.XmlIgnore] above your property. I only wanted to serialize an id of an Doctor class and I did it by making a property:
public String DoctorId
{
get
{
return doctor.Id;
}
set
{
DoctorService doctorService = new DoctorService();
doctor = doctorService.getDoctorById(value);
}
}
When deserializing a set method is called value is equal to serialized doctor's id. Then a service is used which finds the doctor from some other repository and it sets the doctor field in the Visit class.

XML serializing Enum type properties

I'm trying to XML serialize a class containing a enum property. If the property is declared using a specific enum, it works just fine. But I need the property to be of type Enum, so I can set it to different enum types. However, when doing this I get an exception.
The type [namespace].Simple may not be used in this context.
I've tried different attributes on the enum definition, but haven't gotten it right so far. Is there a way to do this?
public enum Simple : byte
{
one = 0x01,
two = 0x02,
three = 0x03
}
public class Foo
{
public Enum Simple { get; set; }
}
public class Program
{
static void Main(string[] args)
{
using (var writer = XmlWriter.Create(Console.OpenStandardOutput()))
{
try
{
var foo = new Foo
{
Simple = Simple.three
};
var serializer = new XmlSerializer(foo.GetType());
serializer.Serialize(writer, foo);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.ReadLine();
}
}
You can try to set EnumMember attrubute on your DataContract that you want to serialize, for more specific info visit
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.enummemberattribute(v=vs.110).aspx
Enum is abstract and cannot be serialized. A possible approach to solve is presented in this answer.
The common primitive base type of enum is int (by default, can also be byte or long for instance).
So you could as well simply use this integer base type instead, like byte Simple in your Foo class.
In case you need the string representation to appear in xml (identical to the enum field name), expose it as string Simple.
Based on dlatikay's idea about splitting the enum into two strings for enum type and member name, I've come up with the following solution. The example converts to from a Foo object to XML string, and back to a new Foo object again.
public enum SimpleEnum : byte
{
one = 0x01,
two = 0x02,
three = 0x03
}
public class Foo
{
private Enum _simple;
[XmlIgnore]
public Enum Simple
{
get { return _simple; }
set {
_simple = value;
var type = Simple.GetType();
var underlyingType = Enum.GetUnderlyingType(type);
EnumType = Simple.GetType().FullName;
EnumMember = Simple.ToString();
}
}
private string _enumType;
public string EnumType
{
get { return _enumType; }
set { _enumType = value; }
}
private string _enumMember;
public string EnumMember
{
get { return _enumMember; }
set {
_enumMember = value;
_simple = (Enum)Enum.Parse(Type.GetType(EnumType), EnumMember);
}
}
}
public class Program
{
static void Main(string[] args)
{
var str = new StringBuilder();
using (var writer = XmlWriter.Create(str))
{
try
{
var foo = new Foo
{
Simple = SimpleEnum.three
};
var serializer = new XmlSerializer(typeof(Foo));
serializer.Serialize(writer, foo);
Console.WriteLine(str.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
using (TextReader reader = new StringReader(str.ToString()))
{
try
{
var serializer = new XmlSerializer(typeof(Foo));
var foo = (Foo)serializer.Deserialize(reader);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.ReadLine();
}
}

Serialization: Dynamic class names

I have already tried various possibilities but maybe I am just too tired of seeing the solution -.-
I have an xml structure like this:
<diagnosisList>
<diagnosis>
<surgery1>
<date>1957-08-13</date>
<description>a</description>
<ops301>0-000</ops301>
</surgery1>
<surgery2>
<date>1957-08-13</date>
<description>a</description>
<ops301>0-000</ops301>
</surgery2>
<surgery...>
</surgery...>
</diagnosis>
</diagnosisList>
As you see there is a variable number of surgeries. I have a class "surgery" containing the XML elements.
class Surgery
{
[XmlElement("date")]
public string date { get; set; }
[XmlElement("description")]
public string description { get; set; }
[XmlElement("ops301")]
public string ops301 { get; set; }
public Surgery()
{
}
}
and a class diagnosis creating the structure by adding the surgery class to the constructor.
diagnosis.cs
class Diagnosis
{
[XmlElement("surgery")]
public Surgery surgery
{
get;
set;
}
public Diagnosis(Surgery Surgery)
{
surgery = Surgery;
}
}
I need to be able to serialize the class name of the surgery dynamically by adding a number before serialization happens.
does anybody know a way to achieve that?
any help is really appreciated :)
Kind regards
Sandro
-- EDIT
I create the whole structure starting from my root class "Import". this class then will be passed to the serializer. So I cannot use XMLWriter in the middle of creation of the structure. I Need to create the whole structure first and finally it will be serialized:
private static void XmlFileSerialization(Import import)
{
string filename = #"c:\dump\trauma.xml";
// default file serialization
XmlSerializer<Import>.SerializeToFile(import, filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
settings.IndentChars = "\t";
XmlSerializer<Import>.SerializeToFile(import, filename, namespaces, settings);
}
and then in the Method "SerializeToFile"
public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces, XmlWriterSettings settings)
{
if (source == null)
throw new ArgumentNullException("source", "Object to serialize cannot be null");
XmlSerializer serializer = new XmlSerializer(source.GetType());
using (XmlWriter xmlWriter = XmlWriter.Create(filename, settings))
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T));
x.Serialize(xmlWriter, source, namespaces);
}
}
}
What I Need is to be able to instantiate a variable number of classes based on the main class "Surgery". The class must have a variable Name, i.e.
surgery1, surgery2, surgery3, etc.
This cannot be changed because this is given by the Institution defining the XML structure.
the class must be accessible by its dynamic Name because the property in the class must be set.
so:
surgery1.Property = "blabla";
surgery2. Property = "babla";
etc.
I am even thinking about using T4 methods to create this part of code, but there must be another way to achieve dynamic class names.
I also thought of creating instances with variable names of the class by using reflection:
System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(string className)
But this doesn't work actually -.-
Does anybody have a hint and could put me in the right direction?
I think, you could try implement methods from IXmlSerializable in object contains diagnosisList.
Try to use custom xml writer and reader.
public class SurgeryWriter : XmlTextWriter
{
public SurgeryWriter(string url) : base(url, Encoding.UTF8) { }
private int counter = 1;
public override void WriteStartElement(string prefix, string localName, string ns)
{
if (localName == "surgery")
{
base.WriteStartElement(prefix, "surgery" + counter, ns);
counter++;
}
else
base.WriteStartElement(prefix, localName, ns);
}
}
public class SurgeryReader : XmlTextReader
{
public SurgeryReader(string url) : base(url) { }
public override string LocalName
{
get
{
if (base.LocalName.StartsWith("surgery"))
return "surgery";
return base.LocalName;
}
}
}
Classes:
[XmlRoot("diagnosisList")]
public class DiagnosisList
{
[XmlArray("diagnosis")]
[XmlArrayItem("surgery")]
public Surgery[] Diagnosis { get; set; }
}
[XmlRoot("surgery")]
public class Surgery
{
[XmlElement("date", DataType = "date")]
public DateTime Date { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("ops301")]
public string Ops301 { get; set; }
}
Use:
var xs = new XmlSerializer(typeof(DiagnosisList));
DiagnosisList diagnosisList;
using (var reader = new SurgeryReader("test.xml"))
diagnosisList = (DiagnosisList)xs.Deserialize(reader);
using (var writer = new SurgeryWriter("test2.xml"))
xs.Serialize(writer, diagnosisList);
Don't mix XML and C#.
You don't need dynamic names in the C# code!
If you need an arbitrary number of instances of a class, create them in a loop and place it in any collection.
var surgeries = new List<Surgery>();
for (int i = 0; i < 10; i++)
{
var surgery = new Surgery();
surgeries.Add(surgery);
}
Later you can access them by index or by enumerating.
surgeries[5]
foreach (var surgery in surgeries)
{
// use surgery
}
As you can see no need dynamic names!
Alternatively, use the dictionary with arbitrary names as keys.
var surgeryDict = new Dictionary<string, Surgery>();
for (int i = 0; i < 10; i++)
{
var surgery = new Surgery();
surgeryDict["surgery" + i] = surgery;
}
Access by name:
surgeryDict["surgery5"]

Serialization of object of one class and setting properties of other class

I am trying to understand the xml serialization/Deserialization behavior in C#. I am working with following example code:
[Serializable]
public class Class1 {
Class2 c2 = new Class2( );
public List<double> Arr2 {
get { return c2.Arr1 ;}
set { c2.Arr1 = value ;}
}
[XmlIgnore]
public Class2 C2 {
get { return c2; }
set { c2 = value; }
}
public Class1( ) {
}
}
public class Class2 {
private List<double> arr1;
public List<double> Arr1 {
get { return arr1; }
set { arr1 = value; }
}
public Class2( ) {
arr1 = (new double[ 5 ]).ToList();
}
}
Every time when I deserialize the xml file for class1, I get zeros in Arr2 instead of values from xml file. I am using following lines for deserialization:
public Class1 c1 = new Class1 () ;
XElement rootnode = XElement.Load( path );
c1 = rootnode.XmlDeserialize<Class1>( "Class1" );
Xml file:
<Class1>
<Arr2>
<double>1</double>
<double>2</double>
<double>3</double>
<double>4</double>
<double>5</double>
</Arr2>
</Class1>
You need to add the [Serializable] attribute to class2.
OK, from the comment, I can see I was wrong.
This worked for me:
XElement rootnode = XElement.Load( path );
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Class1));
var c1 = (Class1)serializer.Deserialize(rootnode.CreateReader());

C# XmlSerialisation addin serialisation

I have written an Interface for writing a very very simple Plugin. In fact it is just a class that is loaded at runtime out of a dll file and is stored as Property in another class. That class that stores the interface has to get serialized. As example this is my serialized object:
<?xml version="1.0" encoding="utf-8"?><MD5HashMapper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.namespace.net" />
But now If i want to load that Object I get an Exception:
As example :
{"<MD5HashMapper xmlns='http://www.vrz.net/Vrz.Map'> was not expected."}
So does anyone has an idea how to solve that problem?
Code:
I have an Interface named IMap that is shared in a dll file to create Addins based on that interface:
public interface IMap
{
object Map(object input);
}
I also have different Mappers (you can pass an input through them and they modify the output). All Mappers are derived from:
[XmlInclude(typeof(ConstMapper))]
[XmlInclude(typeof(FuncMapper))]
[XmlInclude(typeof(IdentMapper))]
[XmlInclude(typeof(NullMapper))]
[XmlInclude(typeof(RefMapper))]
[XmlInclude(typeof(VarMapper))]
[XmlInclude(typeof(TableMapper))]
[XmlInclude(typeof(AddinMapper))]
public class MapperBase:ComponentBase,IMap
{ public virtual object Map(object input) {
throw new NotImplementedException("Diese Methode muss überschrieben werden");
}
public override string ToString() {
return ShortDisplayName;
}
}
Just forget ComponentBase. It is not important for this...
Now i also have a AddinMapper. The main function of that mapper is to cast create MapperBase Object out of the IMap object:
And that is exactly that class I want to seralize including the properties of the Mapper Property (type IMap).
public class AddinMapper : MapperBase
{
private static MapperBase[] _mappers;
const string addinDirectory = #"Addin\Mappers\";
//Mappers from *.dll files are loaded here:
[XmlIgnore]
public static MapperBase[] Mappers
{
get
{
if (_mappers == null)
{
List<MapperBase> maps = new List<MapperBase>();
foreach (string dll in Directory.GetFiles(addinDirectory, "*.dll"))
{
if (Path.GetFileName(dll) != "IMap.dll")
{
var absolutePath = Path.Combine(Environment.CurrentDirectory, dll);
Assembly asm = Assembly.LoadFile(absolutePath);
foreach (Type type in asm.GetTypes().ToList().Where(p => p.GetInterface("IMap") != null))
{
maps.Add(new AddinMapper((IMap)Activator.CreateInstance(type)));
}
}
}
Mappers = maps.ToArray();
}
return _mappers;
}
set
{
_mappers = value;
}
}
IMap _base;
public string MapperString { get; set; }
[XmlIgnore()]
public IMap Mapper
{
get
{
if (_base == null)
{
Type type = null;
foreach (MapperBase mapperBase in Mappers)
{
if (mapperBase is AddinMapper && ((AddinMapper)mapperBase).Mapper.GetType().FullName == _mapperName)
{
type = (mapperBase as AddinMapper).Mapper.GetType();
break;
}
}
if (type != null)
{
XmlSerializer serializer = new XmlSerializer(type);
using (StringReader reader = new StringReader(MapperString))
{
Mapper = (IMap)serializer.Deserialize(reader);
}
}
}
return _base;
}
private set
{
_base = value;
StoreMapperString();
}
}
string _mapperName;
[System.ComponentModel.Browsable(false)]
public string MapperName
{
get
{
return _mapperName;
}
set
{
_mapperName = value;
}
}
public AddinMapper(IMap baseInterface) : this()
{
Mapper = baseInterface;
_mapperName = baseInterface.GetType().FullName;
}
public AddinMapper()
{
}
public override object Map(object input)
{
return Mapper.Map(input);
}
public override string ToString()
{
return Mapper.ToString();
}
private void StoreMapperString()
{
MemoryStream memstream = new MemoryStream();
XmlStore.SaveObject(memstream, Mapper);
using (StreamReader reader = new StreamReader(memstream))
{
memstream.Position = 0;
MapperString = reader.ReadToEnd();
}
}
}
An example for such a addin would be:
public class ReplaceMapper : IMap
{
public string StringToReplace { get; set; }
public string StringToInsert { get; set; }
public object Map(object input)
{
if (input is string)
{
input = (input as string).Replace(StringToReplace, StringToInsert);
}
return input;
}
}
And the Problem is I want to save the Settings like StringToReplace,... as xml
I ve solved my problem:
I really don t know why but take a look at this article: http://www.calvinirwin.net/2011/02/10/xmlserialization-deserialize-causes-xmlns-was-not-expected/
(if link is dead later)
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = elementName;
xRoot.IsNullable = true;
XmlSerializer ser = new XmlSerializer(typeof(MyObject), xRoot);
XmlReader xRdr = XmlReader.Create(new StringReader(xmlData));
MyObject tvd = (MyObject)ser.Deserialize(xRdr);
Now the important thing: It does not matter if you don t get an excption on serialization. You have to add the XmlRootAttribute on both ways: Serialisation and Deserialization.

Categories