Unable to xml serialize custom object - c#

I created a really simple control that contains a list of filter option controls in much the same way that a listbox has a list of listitems.
I'm having some trouble tring to get it to serialize in to viewstate as the serializer appears to be trying to serialize the base class properties and basically i end up with errors like - cannot serialize property "page" What do you think is my problem?
The Code:
[Serializable]
public class FilterOption : Control, ISerializable
{
public event EventHandler Checkchanged;
CheckBox _chk = new CheckBox();
Label _lbl = new Label();
public string Text
{
get { return _lbl.Text; }
set { _lbl.Text = value; }
}
public bool Checked
{
get { return _chk.Checked; }
set { _chk.Checked = value; }
}
public FilterOption()
{
Controls.Add(new LiteralControl("<li>"));
_chk.AutoPostBack = true;
_chk.CssClass = "checkbox";
Controls.Add(_chk);
Controls.Add(_lbl);
_chk.CheckedChanged += new EventHandler(_chk_CheckedChanged);
Controls.Add(new LiteralControl("</li>"));
}
public FilterOption(string Text, bool Checked)
{
Controls.Add(new LiteralControl("<li>"));
_chk.CssClass = "checkbox";
_lbl.Text = Text;
_chk.Checked = Checked;
Controls.Add(_chk);
Controls.Add(_lbl);
_chk.CheckedChanged += new EventHandler(_chk_CheckedChanged);
Controls.Add(new LiteralControl("</li>"));
}
public FilterOption(SerializationInfo info, StreamingContext context)
{
Controls.Add(new LiteralControl("<li>"));
_chk.CssClass = "checkbox";
_lbl.Text = (string)info.GetValue("Text", typeof(string));
_chk.Checked = (bool)info.GetValue("Text", typeof(bool));
Controls.Add(_chk);
Controls.Add(_lbl);
_chk.CheckedChanged += new EventHandler(_chk_CheckedChanged);
Controls.Add(new LiteralControl("</li>"));
}
void _chk_CheckedChanged(object sender, EventArgs e)
{
if (Checkchanged != null)
Checkchanged(this, new EventArgs());
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if(info == null)
throw new System.ArgumentNullException("info");
info.AddValue("Text", _lbl.Text);
info.AddValue("Checked", _chk.Checked);
}
}
I literally only need to serialize the properties added to the serialization info in the GetObjectData method.
I'm using the following code to perform the serialization ...
List<FilterOption> options = new List<FilterOption>();
... add some items to the collection ...
StringWriter writer = new StringWriter();
XmlSerializer ser = new XmlSerializer(typeof(List<FilterOption>));
ser.Serialize(writer, options);
ViewState["Options"] = writer.ToString();
Oh yeh ... i forgot to add ... i got the information from here ...
http://msdn.microsoft.com/en-us/library/ms973893.aspx
(in case it matters)
...
Thx Wardy

First of all you should split your control and serializable data. Second, .net framework contains several serialization types:
Serialization utilities from System.Runtime.Serialization ([BinaryFormatter][1] and [SoapFormatter][2]). Both of this foratters requires [SerializationAttribute][3] for your class or implementing [ISerializable][4] interface (if you need more flexible way controlling serialization process). Those serializers serialize all private fields for current class and all it descendants if that fields does not marked with [NonSerialializedAttribute][5].
Note: this serialization uses during .net remoting.
Xml serialization with [XmlSerializer][6] class. In this case your class should have parameterless constructor and this serializer serialize all public read/write properties for current class and all descendants that does not mark with [XmlIgnoreAttribute][7].
[DataContractSerializer][8]. This serializer requires that you entity should be marked with [DataContractAttribute][9] and all properties should be marked with [DataMemberAttribute][10]. Also this serializer could serialize classes serializable in two previous ways.
In general it's very bad practice try to serialize user control, because it definitely would contains non-serializable fields (that not marked with NonSerializedAttribute). So you'll definitely receive error message during runtime.
The easiest way (and more appropriate from design point of view) is separate serializable data into separate class and choose right serialization technique.
I.e. if you want to use Xml-serialization you should create parameterless constructor for your class and use read/write properties:
public class FilterOption
{
public FilterOption() {}
public string MyLabel{get;set;}
public bool IsChecked{get;set;}
}
and now you could use your previous code:
var options = new List<FilterOption>
{
new FilterOption {MyLabel = "label", IsChecked = false},
new FilterOption {MyLabel = "label2", IsChecked = true}
};
StringWriter writer = new StringWriter();
XmlSerializer ser = new XmlSerializer(typeof(List<FilterOption>));
ser.Serialize(writer, options);

Apparently you cannot serialize a type that inherits a non serializable type even ifyou do not wish to serialize the non-serializable properties of your derived type.
I think this should be classed as a bug since the who point of interfaces like ISerializable is to specify exactly what it is you inted to serialize by manually implementing the method that handles the serialization.
In any case the solution to my particular scenario was to not bother serializing at all and simply save the information of interest in to viewstate which would then be reused on postbacks to rebuild controls in the exact same state ready for the page based postback events to occur.
Shame this isnt better documented somewhere because although microsoft does document the SaveViewState and LoadViewState methods of the page lifecycle they are very vague about how these events might be used, i'm guessing they are hoping someone in the community might provide an example.
I would post my code but its a nasty hack to get everything working so i don't thing it should be mainstream msdn code.
Ok for a small internal app though :)

You should take a look at this link, for XML Serialization
http://msdn.microsoft.com/en-us/library/ms950721.aspx
considering your comments. I have searched again and now I think that you have forgot to add this code in your GetObjectData() function.
base.GetObjectData(si,context);

You cannot serialize your object because it contains objects which aren't serializable. ASP.NET controls (like CheckBox and Label are) are not serializable.
You should create a list of objects instead which contain just the data you really need, which will certainly a boolean value and a string.
You must then recreate the controls from this state on each Post request, but there is no other way I know of.
[Serializable]
public class FilterOption
{
public string MyLabel{get;set;}
public bool IsChecked{get;set}
}
EDIT:
You can put the attribute [NonSerialized] above members you do not want to get serialized.

Related

C# Serialize WinForm

I am trying to serialize a winform, with the end goal of being able to recreate the values in the various controls of the form. My form contains the typical controls, buttons/radio buttons/checkboxes/textboxes/listbox/tab control.
I am receiving this error:
An exception of type 'System.InvalidOperationException' occurred
in System.Xml.dll but was not handled in user code
Additional information: There was an error reflecting type
'Receptionist_Program.Objects.Client.Client_NCQ'.
I setup properties for each value I want to save:
public bool CbMedTreat
{
get { return cbMedTreat.Checked; }
set { cbMedTreat.Checked = value; }
}
public List<Client_AddDoctor> TxtDocExplain // Client_AddDoctor is another form
{
get { return listDoctors; }
set { listDoctors = value; }
}
// etc, variety of string and bool properties
At the top of the class I have the decoration:
[Serializable]
public partial class Client_NCQ : Form
Finally, here is my code doing the serialization:
Client_NCQ badname = new Client_NCQ();
badname.Initialize();
badname.ShowDialog();
string result = "";
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Client_NCQ));
// Error occurs here on above line: new XmlSerializer(typeof(Client_NCQ))
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, badname);
result = textWriter.ToString();
}
I tried two different things so far, first, I added the decoration [XmlIgnore] to the List<> property, this made no difference. Second, I tried ensuring that the constructor was empty and had no parameters.
Serializing an entire Form is a bad idea because it is not meant to be serialized:
it has a lot of properties that should not be serialized (e.g. displaying related properties)
even if it works properly, you will have a lot of data that is not relevant for your application's state
The correct solution is to keep all state information in your custom objects and bind to those objects using WinForm's databinding capabilities. If this means great changes to your application, try to serialize only the data that is relevant to constructing the state.
How can you know which data is relevant for application state?
Before constructing and showing the form, I expect that you are loading the data from a database, file etc. All that information should be contained in clearly defined objects of types marked with [Serializable] attribute. This way, it is easy to serialize and deserialize it at will.
Also, it is important to take into consideration version tolerant serialization or what happens when the form / state information is changed (e.g. a field is added) and an older XML is used to restore state.
Every form has its own mechanism to store and retrieve (serialize and deserialize) data and it does this automatically. However, the following conditions are to be met in order to use this feature automatically.
- All properties which are desired to be serialized must have public get and set accesor.
- If a certain property represents custom object such as user defined class or struct, the object must be adorned with [Serializable] attribute.
- Property which is desired to be serialized must not have [DesignerSerializationVisibility] attribute set to Hidden. By default it is Visible so not specifying this attribute at all is sufficiently serves the purpose.
Consider this example:
namespace MnM.Drawing
{
[Serializable, TypeConverter(typeof(ExpandableObjectConverter))]
public class Coordinates
{
public int X { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Y { get; set; }
public int Z { get; protected set; }
}
public class MyForm : Form
{
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public Coordinates MyCoordinates { get; set; }
}
}
Now, MyForm will automatically serialize MyCoordinates object but...
Only property X will get serialized because it fits the requisite
status to qualify for auto serialization.
Property Y can not be serialized because of DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
Property Z can not get serialized automatically because it does
not have public set accesor.
In order to serialize Y and Z, custom serialization code is required. In most cases, need of custom serialization does not arise and custom serialization can be done in many ways but its a vast topic.

Serialize an object to XML without using attributes

Is it possible to control XmlSerializer/DataContractSerializer behavior without specifying any serialization attributes?
To be more specific, I want to control the serialization (XmlIgnore, custom attribute name etc.), but without decorating my properties.
Use cases:
A large existing class, which I don't wish to pollute with serialization attributes
Serializing a class for which no source code is available
Switching from using XmlSerializer to DataContractSerializer to JSON without changing class code
For example, how would I serialize the following without uncommenting the attributes:
// [Serializable]
public MyClass
{
// [XmlIgnore] - The following property should not be serialized, without uncommenting this line
public int DontSerializeMeEvenThoughImPublic { get; set; }
// [XmlAttribute("another_name")] - should be serialized as 'another_name', not 'SerializeMeAsXmlAttribute'
public double SerializeMeAsXmlAttribute { get; set; }
// [DataMember] - should be included
private string IWantToBeSerializedButDontDecorateMeWithDataMember { get; set; }
}
You can't (do it elegantly).
The only way to modify the way the XmlSerializer serializes classes is by using attributes (by the way SerializableAttribute is not required). The DataContractSerializer is even worse.
One possible solution is to create intermediate classes for XML serialization/desrialization and use Automapper to copy the data between the "real" class and mediator.
I've used this approach to keep the front end XML serialization concerns outside of my business logic data types.
I know this is an old question, but for the XmlSerializer part, it's interesting that no one has suggested the use of Attribute overrides.
Although not solving the Private property, but AFAIK you can't do that with attributes either, so the only route there would be the IXmlSerializable interface.
But what you can do by adding Attributes should be possible with overrides as well.
The following should work for the change wishes reflected by the outcommented XmlAttributes:
public class Program
{
public static void Main()
{
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(MyClass), "DontSerializeMeEvenThoughImPublic", new XmlAttributes { XmlIgnore = true });
overrides.Add(typeof(MyClass), "SerializeMeAsXmlAttribute", new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("another_name") });
XmlSerializer serializer = new XmlSerializer(typeof(MyClass), overrides);
using (var writer = new StringWriter())
{
serializer.Serialize(writer, new MyClass());
Console.WriteLine(writer.ToString());
}
}
}
Serialization via XmlSerializer should work without the attributes.

Find out whether property setter is called in DeSerialization process

Is there a way to find out whether an object property is called as part of the DeSerialization process (e.g. by the XmlSerializationReaderXXX).
Background: A typical scenario is to disable events and complex operations in that case, until the initialization is complete.
One approach I have found, is to "interpret" the stack and look up whether the call is triggered by XmlSerializationReaderXXX, which is not so elegant IMHO. Is there anything better?
public SomeClass SomeProperty
{
get { ..... }
set
{
this._somePropertyValue = value;
this.DoSomeMoreStuff(); // Do not do this during DeSerialization
}
}
-- Update --
As Salvatore has mentioned, somehow similar to How do you find out when you've been loaded via XML Serialization?
I have a possible solution.
public class xxx
{
private int myValue;
[XmlElement("MyProperty")]
public int MyPropertyForSerialization
{
get { return this.myValue; }
set
{
Console.WriteLine("DESERIALIZED");
this.myValue = value;
}
}
[XmlIgnore]
public int MyProperty
{
get { return this.myValue; }
set
{
Console.WriteLine("NORMAL");
this.myValue = value;
}
}
}
class Program
{
static void Main(string[] args)
{
xxx instance = new xxx();
instance.MyProperty = 100; // This should print "NORMAL"
// We serialize
var serializer = new XmlSerializer(typeof(xxx));
var memoryStream = new MemoryStream();
serializer.Serialize(memoryStream, instance);
// Let's print our XML so we understand what's going on.
memoryStream.Position = 0;
var reader = new StreamReader(memoryStream);
Console.WriteLine(reader.ReadToEnd());
// Now we deserialize
memoryStream.Position = 0;
var deserialized = serializer.Deserialize(memoryStream) as xxx; // This should print DESERIALIZED
Console.ReadLine();
}
}
The trick is using the XmlIgnore, it will force the xml serializer to ignore our property, then we use XmlElement to rename the property for serialization with the name of the property we want.
The problem with this technique is that you have to expose a public property for serialization, and is in some way bad because it can virtually be called by everyone.
It will not work if the member is private, unfortunally.
It works, is not totally clean, but is thread safe and don't rely on any flag.
Another possibility is to use something like the Memento pattern.
Using the same trick you can add a property called for example Memento that returns another object that contains properties suitable only for serialization, it can makes things a little cleaner.
Did you think instead of changing approach and using DataContractSerializer? It is much more powerful and produces pure XML. It supports the OnDeserializationCallback mechanism.
Since you got a pretty complex scenario you might want to consider creating a "data core" class which will be actually serialized/deserialized using simple direct way. Then your complex object is constructed from that object and you fire all events/operations as normal. It will make sequence of deserialize -> fire events/operations more explicit and easier to understand.
There's an OnDeserializingAttribute/OnDeserializedAttribute attributes pair. You can set isDeserializing flag while object is being deserialized. I don't know if they play well with XML serialization, though.
For XML Serialization solution could be implementing IXmlSerializable and embedding such logic into the ReadXml()/WriteXml() method
To have finer control of the deserialization process you could implement IXmlSerializable interface for SomeClass - in ReadXML you can then for example have some field set a flag that you are in deserialization... this flag can then be checked in the respective methods... and on completion it needs to be reset.
Another option (though not for XML IIRC) is to implement the above via OnDeserializingAttribute and OnDeserializedAttribute .
I misunderstood the question at first, but you want to ask from within setter if you are called during deserialization. To do that, use a static flag:
[serializable]
class SomeClass
{
public static IsSerializing = false;
SomeProperty
{
set
{
if(IsSerializing) DoYouStuff();
}
}
}
and then set the flag just before the serialization:
try
{
SomeClass.IsSerializing = true;
deserializedClass = (SomeClass)serializer.Deserialize(reader);
}
finaly
{
SomeClass.IsSerializing = false; //make absolutely sure you set it back to false
}
Note that same approach can work even if you deserialize a class that contains a member of your class...
Set a breakpoint on the property, and run in debug mode. It will break at the point of access for the getter/setter that you set the breakpoint on.

Deserialize generic collections - coming up empty

I've got a settings object for my app that has two collections in it. The collections are simple List generics that contain a collection of property bags. When I serialize it, everything is saved with no problem:
XmlSerializer x = new XmlSerializer(settings.GetType());
TextWriter tw = new StreamWriter(#"c:\temp\settings.cpt");
x.Serialize(tw, settings);
However when I deserialize it, everything is restored except for the two collections (verified by setting a breakpoint on the setters:
XmlSerializer x = new XmlSerializer(typeof(CourseSettings));
XmlReader tr = XmlReader.Create(#"c:\temp\settings.cpt");
this.DataContext = (CourseSettings)x.Deserialize(tr);
What would cause this? Everything is pretty vanilla... here's a snippet from the settings object... omitting most of it. The PresentationSourceDirectory works just fine, but the PresentationModules' setter isn't hit:
private string _presentationSourceDirectory = string.Empty;
public string PresentationSourceDirectory {
get { return _presentationSourceDirectory; }
set {
if (_presentationSourceDirectory != value) {
OnPropertyChanged("PresentationSourceDirectory");
_presentationSourceDirectory = value;
}
}
}
private List<Module> _presentationModules = new List<Module>();
public List<Module> PresentationModules {
get {
var sortedModules = from m in _presentationModules
orderby m.ModuleOrder
select m;
return sortedModules.ToList<Module>();
}
set {
if (_presentationModules != value) {
_presentationModules = value;
OnPropertyChanged("PresentationModules");
}
}
}
If a list is deserialized, the setter is never called. Just the getter. The Deserializer just invokes the #Add() method and adds the serialized elements to the exising List. This is behaviour by Design. See MSDN.
You could add a new property wich gets your list without any linq statements.
Add an [XmlIgnore] to your existing property, the XmlSerializer will skip this property now.
[XmlIgnore]
public List<Module> PresentationModules {
Create a new property which exposes the list:
private List<Module> _presentationModules = new List<Module>();
public List<Module> PresentationModulesList {
get { return _presentationModules; }
}
Your event in the setter of PresentationModules will only be invoked if you assign a NEW list to the property. Maybe you should make the setter private.
You can override the serialization and de-serialization methods to add custom information. It's been awhile since I implemented something like this, but I recall having to override to reconstruct some private members from the object upon receiving the serialization data.

Can I add attributes to an object property at runtime?

For example I want to remove or change below property attributes or add a new one. Is it possible?
[XmlElement("bill_info")]
[XmlIgnore]
public BillInfo BillInfo
{
get { return billInfo; }
set { billInfo = value; }
}
(edit - I misread the original question)
You cannot add actual attributes (they are burned into the IL); however, with XmlSerializer you don't have to - you can supply additional attributes in the constructor to the XmlSerializer. You do, however, need to be a little careful to cache the XmlSerializer instance if you do this, as otherwise it will create an additional assembly per instance, which is a bit leaky. (it doesn't do this if you use the simple constructor that just takes a Type). Look at XmlAttributeOverrides.
For an example:
using System;
using System.Xml.Serialization;
public class Person
{
static void Main()
{
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = false;
attribs.XmlElements.Add(new XmlElementAttribute("personName"));
overrides.Add(typeof(Person), "Name", attribs);
XmlSerializer ser = new XmlSerializer(typeof(Person), overrides);
Person person = new Person();
person.Name = "Marc";
ser.Serialize(Console.Out, person);
}
private string name;
[XmlElement("name")]
[XmlIgnore]
public string Name { get { return name; } set { name = value; } }
}
Note also; if the xml attributes were just illustrative, then there is a second way to add attributes for things related to data-binding, by using TypeDescriptor.CreateProperty and either ICustomTypeDescriptor or TypeDescriptionProvider. Much more complex than the xml case, I'm afraid - and doesn't work for all code - just code that uses the component-model.
It is not possible to add/remove attributes from a class at runtime.
It is possible however to update the way XML serialization works at runtime without needing to edit attributes. See Marc's post.
EDIT Updated

Categories

Resources