Serialization of object of one class and setting properties of other class - c#

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

Related

Property: Get: Set: Serialize to XML

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 {}
}

Doesn't deserialize one instance of an object

I have a rather complex data model that gets serialized to and from XML. We all know that when C#'s XmlSerializer serializes the same instance of a object multiple times that objects data get duplicated. I am having an issue of one instance of an object not deserializing. For example, let's say we have three objects: A, B, C. Lets say that A contains two instances of B, and B contains an list of C.
A
->B
->List<C> -- This one deserializes
->B
->List<C> -- This one does not
When I deserialize object A, the first instance of B deserializes correctly. However, when I inspect the second instance of B the List<C> is empty. I have ran a difference compare on the sections XML and they are the same.
Does anyone know why one list would deserialize and not the other?
UPDATE
This is the best that I can pair down the problem. The original XML is about 110,000 lines long.
Classes:
[Serializable]
public class A
{
public B instanceOne {get; set;}
public B instanceTwo {get; set;}
}
[Serializable]
public class B : INotifyPropertyChanged
{
private C _c;
public ObservableCollection<C> C
{
get => _c;
set
{
if(_c == value)
return;
_c = value;
RaisePropertyChanged(nameof(C));
}
}
//More Code
}
[Serializable, XmlRoot(ElementName = "MyC", Namespace = "MyNS")]
public class C
{
public int value {get;set;}
}
XML output:
<?xml version="1.0"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<InstanceOne>
<C>
<Value xmlns="MyNS">10</Value>
</C>
</InstanceOne>
<InstanceTwo>
<C>
<Value xmlns="MyNS">10</Value>
</C>
</InstanceTwo>
</A>
C# Deserialization Code
XmlSerializer xml = new XmlSerializer(typeof(A));
using (FileStream fs = File.OpenRead(#"C:\a.xml"))
{
var t = xml.Deserialize(fs);
}
Lists should have proper XmlElement attributes along with names.
And empty lists (without elements are skipped). It works on multiple levels.
I reproduced this :
[Serializable]
public class A
{
public A()
{
this.instanceOne = new B();
this.instanceTwo = new B();
}
public B instanceOne { get; set; }
public B instanceTwo { get; set; }
}
[Serializable]
public class B
{
public B()
{
this._c = new C();
this._c.value = 10;
}
private C _c;
}
[Serializable, XmlRoot(ElementName = "MyC", Namespace = "MyNS")]
public class C
{
public int value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var serializer = new XmlSerializer(typeof(A));
var a = new A();
try
{
var fs = new System.IO.FileStream("test.xml", System.IO.FileMode.Create);
serializer.Serialize(fs, a);
fs.Close();
A d = (A)serializer.Deserialize(new System.IO.FileStream("test.xml", System.IO.FileMode.Open));
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
I was not able to solve the issue at hand by calling the XmlSerializer. I ended up parsing the file then going back through the XML with the XmlReader to fill in the specific missing gaps.

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

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

C# XML Serialization of an array - Skip "empty" string values

I want to skip the empty element creation in XML file during the xml Serialization in C#.
Ex :
ArrayElements elm = new ArrayElements
{ Items =new string[] "Item1", " ", "Item2", " ", " ", "Items" } };
During the serialization it should skip the empty strings.
This is my class :
[Serializable]
public class ArrayElements
{
[XmlElement(IsNullable=false)]
public string[] Items { get; set; }
}
You can do it with a surrogate property.
namespace Cheeso.Xml.Samples.Arrays
{
static class Extensions
{
private static XmlSerializerNamespaces _ns;
static Extensions()
{
// to suppress unused default namespace entries in the root elt
_ns = new XmlSerializerNamespaces();
_ns.Add( "", "" );
}
public static string SerializeToString(this XmlSerializer s, object o)
{
var builder = new System.Text.StringBuilder();
var settings = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(builder, settings))
{
s.Serialize(writer, o, _ns);
}
return builder.ToString();
}
}
[XmlType("ArrayOfString")]
public class MyContainer
{
[XmlIgnore]
public String[] a;
// surrogate property
[XmlElement("string")]
public String[] A
{
get
{
List<String> _proxy = new List<String>();
foreach (var s in a)
{
if (!String.IsNullOrEmpty(s))
_proxy.Add(s);
}
return _proxy.ToArray();
}
set
{
a = value;
}
}
}
class StringArrayOnlyNonEmptyItems
{
static void Main(string[] args)
{
try
{
Console.WriteLine("\nRegular Serialization of an array of strings with some empty elements:");
String[] x = { "AAA", "BBB", "", "DDD", "", "FFF" };
XmlSerializer s1 = new XmlSerializer(typeof(String[]));
string s = s1.SerializeToString(x);
Console.WriteLine(s);
Console.WriteLine("\nSerialization of an array of strings with some empty elements, via a Container:");
MyContainer c = new MyContainer();
c.a = x;
XmlSerializer s2 = new XmlSerializer(typeof(MyContainer));
s = s2.SerializeToString(c);
Console.WriteLine(s);
}
catch (System.Exception exc1)
{
Console.WriteLine("uncaught exception:\n{0}", exc1);
}
}
}
}
Are you sure this is what you want? One drawback, is that when you deserialize, you will not be able to get back the empty strings, since there's no way for the deserializer to know about them. Usually when you deserialize, you want to get back an instance that looks exactly like the one you originally serialized.
If that is what you want, then you have to tweak your object to suit the serialization process. As Cheeso suggests, a surrogate property is a good solution for this.
Also, just for clarity, am I correct to say you have an object ArrayElements that has a property Items, which is an array of strings?

Categories