I have one class which was serialized before. We have the xml output from it.
When we open the project we deserialize the xml to get the preserved objects.
Now i have added new bool property to class and since it is a new property, old xmls don't have this attribute. My deserialization works fine but assigns false to bool property, I want it to set true if its not there in XML. How can i achieve this ? I tried like this
public bool? _flag;
[XmlElement("Flag")]
public bool? flag
{
get
{
if (null != _flag)
{
return _flag;
}
return true;
}
set { _flag= value; }
}
You just need to add your default constructor and set it there. Here is an example:
public MyObject()
{
Flag = true;
}
EDIT
I'm not sure what's going on in your code, but this works perfectly fine:
public class MyObject
{
public MyObject()
{
Flag = true;
}
public bool Flag { get; set; }
public string Name { get; set; }
}
First, I didn't have the bool property there and serialized it to a file.. then for step 2, I added that bool property and the constructor.. then deserialized it from disk and it showed true, which is what I expected.
Please review your code, as I expect something else is going on. If you need help, post the full class here.
Related
I have a simple class that is intended for options of an winforms application. There should be a method that reset options to their default values. I know I can add a separate method to take care of this, but the code will be huge (If I add more options to the class) :
public SensorOptions()
{
ShowLabelMax = ShowLabelMin = ShowLabelAvr = ShowReceivedTextBox = true;
ChartMaxValue = 140;
ChartMinValue = -40;
ShowChartMinValue = ShowChartMaxValue = ShowChartAvrValue = ShowChartAvrLine = true;
LogFolder = Environment.SpecialFolder.MyDocuments.ToString();
LoggingEnabled = true;
}
public void ResetOptions()
{
this = new SensorOptions(); //can not do. 'this' is read-only
}
I mean I can copy/paste the code from constructor into ResetOptions() method. But is there any smarter ways to achieve this?
You cannot assign this because you may have references to this instance of your class in your program. If you could re-construct the object by re-assigning this, it would mean that all references to the old instance of the class become invalid.
No matter how many options you have in your class, you initialize each of them one or the other way (because you mention default value in your question - so you need to assign that default value somewhere at least once, probably in the constructor). Therefore, the solution to your problem is simple - move all initializers to the separate method and call it in the constructor, and then also call it every time you need to reset your options to their default values.
If any of your options are not assigned a default value explicitly, and use system default and you don't want to write option=default(optionType) for each option, you can use reflection to enumerate all fields/properties in that class and assign default values to them, like this:
public static object GetDefault(Type type)
{
if(type.IsValueType) return Activator.CreateInstance(type);
return null;
}
foreach(var field in this.GetType().GetFields())
field.SetValue(this, GetDefault(field.FieldType));
foreach(var prop in this.GetType().GetProperties())
prop.SetValue(this, GetDefault(prop.PropertyType));
Move all of the code from the constructor into the ResetOptions method, then in your constructor call the ResetOptions method. Your initialisiation code is only in one place then.
You have very simple architecture for your situation. In my opinion it would be better to apply a trick for this:
you have class for holding all your options (pseudo code):
class AllOptionsBackstage
{
public bool ShowLabelMax { get; set; }
public bool ShowLabelMin { get; set; }
public bool ShowLabelAvr { get; set; }
public AllOptionsBackstage()
{
// apply default values here
}
}
.....
class MyOptions
{
private AllOptionsBackstage _options;
public MyOptions()
{
Reset();
}
public bool ShowLabelMax
{
get{ return _options.ShowLabelMax; }
set{ _options.ShowLabelMax = value; }
}
public bool ShowLabelMin
{
get{return _options.ShowLabelMin;}
set{_options.ShowLabelMin=value; }
}
public bool ShowLabelAvr
{
get{ return _options.ShowLabelAvr;}
set{ _options.ShowLabelAvr = value; }
}
public void Reset()
{
_options = new AllOptionsBackstage(); // will reset all your options to default
}
}
I have the following class:
public class FtpDefinition
{
public FtpDefinition()
{
Id = Guid.NewGuid();
FtpServerAddress = string.Empty;
FtpPortSpecified = false;
FtpPort = "21";
}
[System.Xml.Serialization.XmlElement("Id")]
public System.Guid Id { get; set; }
[System.Xml.Serialization.XmlElement("FtpServerAddress")]
public string FtpServerAddress { get; set; }
[System.Xml.Serialization.XmlElement("FtpPortSpecified")]
public bool FtpPortSpecified { get; set; }
[System.Xml.Serialization.XmlElement("FtpPort")]
public string FtpPort { get; set; }
}
I have a method that gets the following XML string, and using the .net XML deserialization capability
deserializes it into an object of type FtpDefinition.
<FTPDefinition>
<Id>a0a940a7-6785-41be-ac3a-75ba5d4c13ee</Id>
<FtpServerAddress>ftp.noname.com</FtpServerAddress>
<FtpPortSpecified>false</FtpPortSpecified>
<FtpPort>21</FtpPort>
</FTPDefinition>
The problem is, that although the Id and FtpServerAddress fields get populated properly, FtpPort gets
populated with an empty string, and what's more weird is that FtpPortSpecified gets populated with the bool value TRUE instead of FALSE.
I replaced the automatic properties in the above code with actual return\... = value old style getter\setter, so that I can catch the setter getting hit. I was suspecting there's some user code setting the value, but this is not the case. In the call stack it clearly shows that the .net deserialization code is calling the setter with the value TRUE, but one can also see that the XML string provided as parameter to the deserializing method has the correct value (FALSE).
The deserialization code is simple:
XmlSerializer xs = ...(objectType);
using (StringReader stringReader = new StringReader(xml))
{
return xs.Deserialize(stringReader);
}
Please help me figure out what's going on.
The Specified suffix has some special behavior in XML Serialization. Simply change FtpPortSpecified to something else.
http://msdn.microsoft.com/en-us/library/office/bb402199(v=exchg.140).aspx
I need to extend the Omu.ValueInjecter to perform a check before a property assignment is made. Given the code example below, assignment of prop A should only occur if SetA is true. I suspect LoopValueInjection is not the right base class here, but could someone correct the code below so that I can check SetA during the injection process?
var source = new Source() { A = 3 };
var dest = new Dest();
dest.InjectFrom<MyInjector>(source);
public class Source
{
public int A { get; set; }
public bool SetA { get; set; }
}
public class Dest
{
public int A { get; set; }
}
public class MyInjector : LoopValueInjection // or some other base class!
{
protected override bool AllowSetValue(object value)
{
// check SetA!!
//return base.AllowSetValue(value);
}
}
Ok, I have it working now. Below is the correct code. I missed the UseSourceProp overload which served my purposes exactly.
The problem I was trying to solve was with MVC after a view model is posted to an action, you must copy the view model data into the data model. When the data model is initialized there could be certain defaults that are set. And when the view model was injected, those defaults would be overwritten. It would be correct to overwrite those if the view model properties had been set, but I had default values being overwritten by view model values that had not been set from a post operation.
The solution was to put a flag in the view model that would indicate whether a property had been set are not. And the setter for each property I simply updated a common list string object in the base class.
In the code below in the UseSourceProp method, you can see that if the property name being processed does not exist in SetProperties, then the method returns false and the property is not set.
var source = new Source() { A = 3 };
var dest = new Dest();
dest.InjectFrom<MyInjector>(source);
public class Source
{
public int A { get; set; }
public bool SetA { get; set; }
}
public class Dest
{
public int A { get; set; }
}
public class MyInjector : LoopValueInjection // or some other base class!
{
protected override void Inject(object source, object target)
{
if (source is BaseEntityViewModel) _baseEntityViewModel = (BaseEntityViewModel)source;
base.Inject(source, target);
}
protected override bool UseSourceProp(string sourcePropName)
{
if (_baseEntityViewModel is BaseEntityViewModel)
return _baseEntityViewModel.SetProperties.Contains(sourcePropName);
else
return base.UseSourceProp(sourcePropName);
}
}
I think overridding the SetValue method might be what you need. This is a slight modification of the docs from here: http://valueinjecter.codeplex.com/wikipage?title=Getting%20started&referringTitle=Documentation and http://valueinjecter.codeplex.com/discussions/355101
public class MyInjector : LoopValueInjection
{
//by default is return sourcePropertyValue; override to change behaviour
protected override object SetValue(ConventionInfo c)
{
// this is just a sample, but you could write anything here
return new Dest
{
//Check if source value is true and only then set property
if(c.SourceProp.Name == "SetA")
{
var setASourceVal = c.TargetProp.Value As bool;
if(setASourceVal)
{
A = sourcePropertyValue;
}
}
}
}
}
depends which injection your using,
with ConventionInjection you have the value in the Match method
https://valueinjecter.codeplex.com/wikipage?title=step%20by%20step%20explanation&referringTitle=Home
for the LoopValueInjection you can override AllowSetValue
the latest (fastest) injection is this: https://valueinjecter.codeplex.com/wikipage?title=SmartConventionInjection&referringTitle=Home
It has one limitation comparing to the ConventionInjection, you don't have the values of the Source and Target Properties in the Match method but you have them in the SetValue Method and you can cancel the setting of the value to that property if you set false to the ref parameter setValue
So I have the following method:
private int? myIntField
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public int? IntField{
get {
return this.myIntField;
}
set {
this.myIntField= value;
}
}
Now, I am deserializing xml from a post, if for whatever reason I am getting a string, such as "here is the int field: 55444" instead of 55444, the error I get in response is: Input string was not in a correct format. which isn't very specific, especially considering I will have more than one int field I need to verify.
Originally, I was planning something like this:
private string myIntField
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
public int? IntField{
get {
return this.myIntField.CheckValue();
}
set {
this.myIntField= value;
}
}
Where CheckValue performs a try-parse to an Int32, and if it fails it returns a null and adds an error to a list. However, I can't seem to nail this set-up for the generated classes.
Is there I way I can throw a specific error if I am getting strings in place of ints, DateTimes, etc?
It's easy if you have schema(s) for you XML and validate it against schema before deserializing. Suppose you have schema(s) for your XML, you can initialize a XmlSchemaSet, add your schema(s) in it and the:
var document = new XmlDocument();
document.LoadXml(xml); // this a string holding the XML
document.Schemas.XmlResolver = null; //if you don't need to resolve every references
document.Schemas.Add(SchemaSet); // System.Xml.Schema.XmlSchemaSet instance filled with schemas
document.Validate((sender, args) => { ... }); //args are of type ValidationEventArgs and hold problem if there is one...
Personally I think this is a better approach, because you can validate your XML before deserializing and be sure the XML is correct otherwise the deserializer will most probably throw an exception if something is wrong and you will almost never be able to show a meaningful feedback to the user...
P.S. I recommend creating schema(s) describing the XML
The "Input string was not in a correct format" messages comes from a standard System.FormatException raised by a call to int.Parse, added to the automatically generated assembly that does the deserialization. I don't think you can add some custom logic to that.
One solution is to do something like this:
[XmlElement("IntField")]
[Browsable(false)] // not displayed in grids
[EditorBrowsable(EditorBrowsableState.Never)] // not displayed by intellisense
public string IntFieldString
{
get
{
return DoSomeConvert(IntField);
}
set
{
IntField = DoSomeOtherConvert(value);
}
}
[XmlIgnore]
public int? IntField { get; set; }
It's not perfect, because you can still get access to the public IntFieldString, but at least, the "real" IntField property is used only programmatically, but not by the XmlSerializer (XmlIgnore), while the field that's holding the value back & forth is hidden from programmers (EditorBrowsable), grids (Browsable), etc... but not from the XmlSerializer.
I have three approaches for you.
Assuming your data is being entered by a user in a user interface, use input validation to ensure the data is valid. It seems odd to allow random strings to be entered when it should be an integer.
Use exactly the approach you suggest above. Here's an example using LINQ Pad
void Main()
{
using(var stream = new StringReader(
"<Items><Item><IntValue>1</IntValue></Item></Items>"))
{
var serializer = new XmlSerializer(typeof(Container));
var items = (Container)serializer.Deserialize(stream);
items.Dump();
}
}
[XmlRoot("Items")]
public class Container
{
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
public class Item
{
[XmlElement("IntValue")]
public string _IntValue{get;set;}
[XmlIgnore]
public int IntValue
{
get
{
// TODO: check and throw appropriate exception
return Int32.Parse(_IntValue);
}
}
}
Take control of serialization using IXmlSerializable, here's another example
void Main()
{
using(var stream = new StringReader(
"<Items><Item><IntValue>1</IntValue></Item></Items>"))
{
var serializer = new XmlSerializer(typeof(Container));
var items = (Container)serializer.Deserialize(stream);
items.Dump();
}
}
[XmlRoot("Items")]
public class Container
{
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
public class Item : IXmlSerializable
{
public int IntValue{get;set;}
public void WriteXml (XmlWriter writer)
{
writer.WriteElementString("IntValue", IntValue.ToString());
}
public void ReadXml (XmlReader reader)
{
var v = reader.ReadElementString();
// TODO: check and throw appropriate exception
IntValue = int.Parse(v);
}
public XmlSchema GetSchema()
{
return(null);
}
}
I tried something like this:
[NonSerialized]
private string _DecodeText;
public string DecodeText { get { return _DecodeText; } set { _DecodeText = value; } }
But it does not work. "DecodeText" is still in the serialized file. How can i prevent the property from serializing?
I Suspect you're using the XmlSerializer? If so use the [XmlIgnore] attribute instead.
This should be applied to the property instead of the backing field as the XmlSerializer serializes public fields and properties (whereas the BinaryFormatter uses refelction to get at the private fields - hence the marking of the private field with NonSerialized when using a BinaryFormatter).
I was able to use the following and not have the property serialized (.NET 4.0):
private string _DecodeText;
[System.Xml.Serialization.XmlIgnore]
public string DecodeText { get { return _DecodeText; } set { _DecodeText = value; } }
Updated Answer
The [NonSerialized] atttibute is on the variable not the property, but it cannot be on the attribute. So it is not going to help.
One way to prevent the property being serialized is to add a method
public bool ShouldSerializeDecodeText() {
return false;
}
and this (for the XmlSerializer at least) will prevent the property being serialized.
If you don't want to add lots of methods to the class just for serialization you might try inheriting from it and adding the methods to the derived class.
hth,
Alan.
I built on top of #John's answer and modified ef.tt template
to include [System.Xml.Serialization.XmlIgnore]
Here is the code
foreach (var navigationProperty in navigationProperties)
{
if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
{
#>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
[System.Xml.Serialization.XmlIgnore]
<#
}
#>
<#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#
}
I think this code that will be help you all. With properties you declared and you want it to be serialized only. Then you should add a method return type as boolean and name method is ShouldSerialize as prefix with [NameProperty]. A scratch code as below and link reference to Newtonsoft for you:
public class DisplayFieldSetting
{
public bool ShouldSerializeHidden()
{
return false;
}
public bool ShouldSerializeKeepOriginialColumnName()
{
return false;
}
public string Hidden { get; set; }
public string KeepOriginialColumnName{ get; set; }
}