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.
Related
I have a class called Menu. Menu has a private list variable _Sections and a public list variable sections. I am using the following pattern for lazy loading. This also allows me to retrieve child items when serializing
public class Menu
{
public int Id;
private List<MenuSection> _Sections = null;
public List<MenuSection> Sections
{
get
{
return _Sections ?? (_Sections = MenuSection.GetListByMenu(Id)); //database call
}
set
{
_Sections = value;
}
}
}
I then modify the collection client side and send it back to the API as JSON.
My problem is that the getter for Sections is called before the setter. This means that the collection is refilled from the database and then has my now updated sections appended.
I've created the following work around, but it's ugly and I don't want to have to remember to do it everywhere I want to lazy load.
public class Menu
{
public int Id;
private bool deserialized = false;
[OnDeserializing()]
internal void OnDeserializingMethod(StreamingContext context)
{
DeserializerCalled = true;
}
private List<MenuSection> _Sections = null;
public List<MenuSection> Sections
{
get
{
return _Sections ?? (_Sections = DeserializerCalled ? new List<>() : MenuSection.GetListByMenu(Id));
}
set
{
_Sections = value;
}
}
}
Am I missing some property attribute or global JSON setting that will resolve this for me?
The problem is that your getter is actually setting the variable, you are just fighting the framework. The get block should just return the value of the _Sections collection. Why not considering in using the Lazy construct introduced in C# 4 ?
private Lazy<List<MenuSection>> _someVariable = new Lazy<List<MenuSection>>(() => MenuSection.GetListByMenu(id));
public string SomeVariable => _someVariable
As a side note, I suggest to follow the right naming convention here and have the private member in camel case: _sections.
I have come across a situation where I probably needed to add properties(of a class) in a list to invoke them manually(or you can say, I need to assign there values(setter)). That is why because, I don't even know which properties is to set the values, but they are decided at runtime. So far I am trying to find out the solution here and there but still I don't get any article that even hints me a work around for this purpose.
Here's what I want to do exactly (mentioned as comments)-
public class DemoClass
{
IList<Properties> _listOfProps;
private int _iFirstProperty;
private string _iSecondProperty;
public DemoClass()
{
_listOfProps = new List<Properties>();
}
public int FirstProperty
{
get
{
return _iFirstProperty;
}
set
{
_iFirstProperty = value;
// Here I want to add this property into the list.
_listOfProps.Add(FirstProperty);
RaisePropertyChanged("FirstProperty");
}
}
public string SecondProperty
{
get
{
return _iSecondProperty;
}
set
{
_iSecondProperty = value;
RaisePropertyChanged("SecondProperty");
}
}
public void HandleChangedProperties()
{
foreach (var list in _listOfProps)
{
// Here I want to invoke the property. ie. sets the 'value' of this property.
list.Invoke(value)
}
}
}
I know, I can use Func to add in the list like- but I can't go with this.
List<Func<int>> listOfFunc = new List<Func<int>>();
listOfFunc.Add(() => { return 0; }); // Adds using lambda expression
listOfFunc.Add(temp); // Adds as a delegate invoker
private int temp()
{
return 0;
}
from MSDN
Properties can be used as if they are public data members, but they
are actually special methods called accessors.
if properties are internally methods, Why they can't be added as List of Func<>
Also, if there's no way I can do that without using Reflection (by getting PropertyInfo list), why Microsoft hasn't designed this in C#?
You can either keep a list of PropertyInfo values and later set the value of the properties using reflection, or you can keep a list of setter delegates (which effectively just forward the value to the real, hidden setter).
For example:
IList<Action<object>> listOfSetters;
listOfSetters.Add(o => this.FirstProperty = (int)o);
// and then:
listOfSetters[0](42); // FirstProperty = 42
I am currently using a LINQ query to read an XML file e.g.
<MyObjects>
<MyObject>
<MyElement>some_text</MyElement>
<MyOtherElement>some_more_text</MyOtherElement>
</MyObject>
</MyObjects>
into a list of custom objects containing custom HistoryString properties. HistoryString contains 2 strings, a currentValue and a previousValue.
This all works great except when using XmlSerializer to write the custom objects back to an XML file, the output fairly obviously contains additional tags i.e.
<MyObjects>
<MyObject>
<MyElement>
<currentValue>some_text</currentValue>
<previousValue>some_text</previousValue>
</MyElement>
<MyOtherElement>
<currentValue>some_more_text</currentValue>
<previousValue>some_more_text</previousValue>
</MyOtherElement>
</MyObject>
</MyObjects>
Q: What would be the neatest and/or most efficient way of reading and writing XML in the same format, based on this fundamental difference?
Some initial ideas:
1) Mark the previousValue property with [System.Xml.Serialization.XmlIgnore] then sweep through the XML string that is to be written removing all traces of <currentValue> and </currentValue>
2) Open the existing file and manually make any updates/deletes/additions - this is surely more long winded.
3) Any way of having a HistoryString automatically resolve to its currentValue rather than serialize each of its properties, similar to how ToString() works?
I have done some research into this, including the useful MSDN articles here and here but I can't see any other attributes that would solve this problem, I am still unsure whether this is possible. Any ideas?
Here is another idea. If you define your class like so:
[Serializable]
public class MyObject
{
[XmlElement(ElementName = "MyElement")]
public string CurrentValueElement
{
get
{
return Element.CurrentValue;
}
set
{
Element = new MyElement
{
CurrentValue = value, PreviousValue = value
};
}
}
[XmlElement(ElementName = "MyOtherElement")]
public string CurrentValueOtherElement
{
get
{
return OtherElement.CurrentValue;
}
set {}
}
[XmlIgnore]
public MyElement Element { get; set; }
[XmlIgnore]
public MyElement OtherElement { get; set; }
}
Then, when the object is serialized, the output XML will look exactly like your example.
Also, if you extend the CurrentValueElement/CurrentValueOtherElement setter like this:
[XmlElement(ElementName = "MyElement")]
public string CurrentValueElement
{
get
{
return Element.CurrentValue;
}
set
{
Element = new MyElement
{
CurrentValue = value, PreviousValue = value
};
}
}
Then you'll be able to use the XmlSerializer to deserialize your objects directly without needing to resorting to LINQ.
Well why not serialize back using original schema and feeding into it the list of transformed objects from history using only current value?
e.g.
from h in HistoryEntryList
select new OriginalEntry{ field = h.field.current_value, ... };
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.
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