I am using DefaultValue attribute for the proper PropertyGrid behavior (it shows values different from default in bold). Now if I want to serialize shown object with the use of XmlSerializer there will be no entries in xml-file for properties with default values.
What is the easiest way to tell XmlSerializer to serialize these still?
I need that to support "versions", so when I change default value later in the code - serialized property gets value it had serialized with, not "latest" one. I can think about following:
Override behavior of PropertyGrid (use custom attribute, so it will be ignoreed by XmlSerializer);
Do sort of a custom xml-serialization, where ignore DefaultValue's;
Do something with object before passing it to XmlSeriazer so it won't contain DefaultValue's anymore.
But there is a chance I miss some secret property what allows to do it without much pain =D.
Here is an example of what I want:
private bool _allowNegative = false;
/// <summary>
/// Get or set if negative results are allowed
/// </summary>
[Category(CategoryAnalyse)]
[Admin]
[TypeConverter(typeof(ConverterBoolOnOff))]
//[DefaultValue(false)] *1
public bool AllowNegative
{
get { return _allowNegative; }
set
{
_allowNegative = value;
ConfigBase.OnConfigChanged();
}
}
//public void ResetAllowNegative() { _allowNegative = false; } *2
//public bool ShouldSerializeAllowNegative() { return _allowNegative; } *3
//public bool ShouldSerializeAllowNegative() { return true; } *4
If I uncomment (*1), then I have desired effect in PropertyGrid - properties with default values are displayed in normal text, otherwise text is bold. However XmlSerializer will NOT put properties with default value into xml-file and this is BAD (and I am trying to fix it).
If I uncomment (*2) and (*3), then it's totally same as uncommenting (*1).
If I uncomment (*2) and (*4), then XmlSerializer will always put properties into xml-file, but this happens because they do not have default value anymore and PropertyGrid shows all values in bold text.
As long as you don't need attributes in your Xml, if you use the DataContractSerializer instead you will get the behavior you desire.
[DataContract]
public class Test
{
[DataMember]
[DefaultValue(false)]
public bool AllowNegative { get; set; }
}
void Main()
{
var sb2 = new StringBuilder();
var dcs = new DataContractSerializer(typeof(Test));
using(var writer = XmlWriter.Create(sb2))
{
dcs.WriteObject(writer, new Test());
}
Console.WriteLine(sb2.ToString());
}
produces (minus namespaces etc)
<Test>
<AllowNegative>false</AllowNegative>
</Test>
You could use two properties:
// For property grid only:
[Category(CategoryAnalyse)]
[TypeConverter(typeof(ConverterBoolOnOff))]
[DefaultValue(false)]
[XmlIgnore]
public bool AllowNegative
{
get { return _allowNegative; }
set
{
_allowNegative = value;
ConfigBase.OnConfigChanged();
}
}
// For serialization:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[TypeConverter(typeof(ConverterBoolOnOff))]
[XmlElement("AllowNegative")]
public bool AllowNegative_XML
{
get { return _allowNegative; }
set
{
_allowNegative = value;
ConfigBase.OnConfigChanged();
}
}
I believe what you are looking for is ShouldSerialize() and Reset(). Using these will expand your class a bit more (with two functions per property), however, it achieves specifically what you are looking for.
Here's a quick example:
// your property variable
private const String MyPropertyDefault = "MyValue";
private String _MyProperty = MyPropertyDefault;
// your property
// [DefaultValueAttribute("MyValue")] - cannot use DefaultValue AND ShouldSerialize()/Reset()
public String MyProperty
{
get { return _MyProperty; }
set { _MyProperty = value; }
}
// IMPORTANT!
// notice that the function name is "ShouldSerialize..." followed
// by the exact (!) same name as your property
public Boolean ShouldSerializeMyProperty()
{
// here you would normally do your own comparison and return true/false
// based on whether the property should be serialized, however,
// in your case, you want to always return true when serializing!
// IMPORTANT CONDITIONAL STATEMENT!
if (!DesignMode)
return true; // always return true outside of design mode (is used for serializing only)
else
return _MyProperty != MyPropertyDefault; // during design mode, we actually compare against the default value
}
public void ResetMyProperty()
{
_MyProperty = MyPropertyDefault;
}
Note that because you want to keep the PropertyGrid functionality in tact, you must know whether you are serializing or not when the ShouldSerialize() function is called. I suggest you implement some sort of control flag that gets set when serializing, and thus always return true.
Please note that you cannot use the DefaultValue attribute in conjunction with the ShouldSerialize() and Reset() functions (you only use either or).
Edit: Adding clarification for the ShouldSerialize() function.
Because there is currently no way to serialize a default value and let the PropertyGrid know that a property has its default value, you must implement a condition checking whether you are in design mode.
Assuming your class derives from a Component or Control, you have a DesignMode property which is set by Visual Studio at design time only. The condition looks as follows:
if (!DesignMode)
return true; // always return true outside of design mode (is used for serializing only)
else
return _MyProperty != MyPropertyDefault; // during design mode, we actually compare against the default value
Edit 2: We're not talking about Visual Studio's design mode.
With the above code in mind, create another property called IsSerializing. Set the IsSerializing property to true before calling XmlSerializer.Serialize, and unset it after.
Finally, change the if (!DesignMode) conditional statement to be if (IsSerializing).
This behaviour of the XmlSerializer can can be overwritten with
XmlAttributeOverrides
I borrowed the idea from here:
static public XmlAttributeOverrides GetDefaultValuesOverrides(Type type)
{
XmlAttributeOverrides explicitOverrides = new XmlAttributeOverrides();
PropertyDescriptorCollection c = TypeDescriptor.GetProperties(type);
foreach (PropertyDescriptor p in c)
{
AttributeCollection attributes = p.Attributes;
DefaultValueAttribute defaultValue = (DefaultValueAttribute)attributes[typeof(DefaultValueAttribute)];
XmlIgnoreAttribute noXML = (XmlIgnoreAttribute)attributes[typeof(XmlIgnoreAttribute)];
XmlAttributeAttribute attribute = (XmlAttributeAttribute)attributes[typeof(XmlAttributeAttribute)];
if ( defaultValue != null && noXML == null )
{
XmlAttributeAttribute xmlAttribute = new XmlAttributeAttribute(attribute.AttributeName);
XmlAttributes xmlAttributes = new XmlAttributes();
xmlAttributes.XmlAttribute = xmlAttribute;
explicitOverrides.Add(userType, attribute.AttributeName, xmlAttributes);
}
}
return explicitOverrides;
}
And made my self an an Attribute to decorate the classes which should emit the default values.
If you want do this for all classes, I'm sure you can adapt the whole concept.
Public Class EmitDefaultValuesAttribute
Inherits Attribute
Private Shared mCache As New Dictionary(Of Assembly, XmlAttributeOverrides)
Public Shared Function GetOverrides(assembly As Assembly) As XmlAttributeOverrides
If mCache.ContainsKey(assembly) Then Return mCache(assembly)
Dim xmlOverrides As New XmlAttributeOverrides
For Each t In assembly.GetTypes()
If t.GetCustomAttributes(GetType(EmitDefaultValuesAttribute), True).Count > 0 Then
AddOverride(t, xmlOverrides)
End If
Next
mCache.Add(assembly, xmlOverrides)
Return xmlOverrides
End Function
Private Shared Sub AddOverride(t As Type, xmlOverrides As XmlAttributeOverrides)
For Each prop In t.GetProperties()
Dim defaultAttr = prop.GetCustomAttributes(GetType(DefaultValueAttribute), True).FirstOrDefault()
Dim xmlAttr As XmlAttributeAttribute = prop.GetCustomAttributes(GetType(XmlAttributeAttribute), True).FirstOrDefault()
If defaultAttr IsNot Nothing AndAlso xmlAttr IsNot Nothing Then
Dim attrs As New XmlAttributes '= {New XmlAttributeAttribute}
attrs.XmlAttribute = xmlAttr
''overide.Add(t, xmlAttr.AttributeName, attrs)
xmlOverrides.Add(t, prop.Name, attrs)
End If
Next
End Sub
Because xsd.exe produces partial classes you can add this EmitDefaultValuesAttribute in seperate a file:
<EmitDefaultValuesAttribute()>
Public MyClass
Public Property SubClass() As MySubClass
End Class
<EmitDefaultValuesAttribute()>
Public MySubClass
End Class
Usage is as follows:
Dim serializer As New XmlSerializer(GetType(MyClass), EmitDefaultValuesAttribute.GetOverrides(GetType(MyClass).Assembly))
Related
I am confused on how XmlSerializer works behind the scenes. I have a class that deserializes XML into an object. What I am seeing is for the following two elements that are NOT part of the Xml being deserialized.
[XmlRootAttribute("MyClass", Namespace = "", IsNullable = false)]
public class MyClass
{
private string comments;
public string Comments
{
set { comments = value; }
get { return comments; }
}
private System.Collections.Generic.List<string> tests = null;
public System.Collections.Generic.List<string> Tests
{
get { return tests; }
set { tests = value; }
}
}
Let's take the following XML as an example:
<MyClass>
<SomeNode>value</SomeNode>
</MyClass>
You notice that Tests and Comments are NOT part of the XML.
When this XML gets deserialized Comments is null(which is expected) and Tests is an empty list with a count of 0.
If someone could explain this to me it would be much appreciated. What I would prefer is that if <Tests> is missing from the XML then the list should remain null, but if a (possibly empty) node <Tests /> is present then the list should get allocated.
What you are observing is that members referring to modifiable collections such as List<T> are automatically pre-allocated by XmlSerializer at the beginning of deserialization. I am not aware of any place where this behavior is documented. It may be related to the behavior described in this answer to XML Deserialization of collection property with code defaults, which explains that, since XmlSerializer supports adding to get-only and pre-allocated collections, if a pre-allocated collection contains default items then the deserialized items will be appended to it - possibly duplicating the contents. Microsoft may simply have chosen to pre-allocate all modifiable collections at the beginning of deserialization as the simplest way of implementing this.
The workaround from that answer, namely to use a surrogate array property, works here as well. Since an array cannot be appended to, XmlSerializer must accumulate all the values and set them back when deserialization is finished. But if the relevant tag is never encountered, XmlSerializer apparently does not begin accumulating values and so does not call the array setter. This seems to prevent the default pre-allocation of collections that you don't want:
[XmlRootAttribute("MyClass", Namespace = "", IsNullable = false)]
public class MyClass
{
private string comments;
public string Comments
{
set { comments = value; }
get { return comments; }
}
private System.Collections.Generic.List<string> tests = null;
[XmlIgnore]
public System.Collections.Generic.List<string> Tests
{
get { return tests; }
set { tests = value; }
}
[XmlArray("Tests")]
public string[] TestsArray
{
get
{
return (Tests == null ? null : Tests.ToArray());
}
set
{
if (value == null)
return;
(Tests = Tests ?? new List<string>(value.Length)).AddRange(value);
}
}
}
Sample .Net fiddle showing that Tests is allocated only when appropriate.
When you apply [System.Xml.Serialization.XmlElement(IsNullable = true)] to the property, the List will be null after deserialization.
Another possibility is to use the "magic" "Specified" suffix:
public bool TestsSpecified {get;set;}
If you have a serialized field/property XXX and a boolean property XXXSpecified, then the bool property is set according to whether or not the main field/property was set.
We wound up here after a google search for the same issue.
What we ended up doing was checking for Count == 0, after deserialization, and manually setting the property to null;
...
var varMyDeserializedClass = MyXmlSerializer.Deserialize(new StringReader(myInput));
if (varMyDeserializedClass.ListProperty.Count == 0)
{
varMyDeserializedClass.ListProperty = null;
}
...
It's a cheap workaround, but provides the expected result and is useful to avoid refactoring or redesign.
I have to following in vb.net and am having a rough time converting to c#. The fieldlist is a class that has several properties however only 2 are relevant for the below procedure. The code to call the procedure looks like myClass.Fields("Test") = 123456. The converters all drop the fieldname from the property. If I add it back then I get The name 'value' does not exist in the current context.
VB.net - works fine in VB
Public WriteOnly Property Fields(ByVal fieldName As String) As Object
Set(ByVal value As Object)
If mvarFieldData Is Nothing Then mvarFieldData = New FieldList
If mvarFieldData(fieldName) Is Nothing Then
mvarFieldData.Add(fieldName, value)
Else
mvarFieldData(fieldName).FieldValue = value
End If
End Set
End Property
c# - I'm doing something wrong here.
public object Fields(string fieldName)
{
set {
if (mvarFieldData == null)
{mvarFieldData = new FieldList();}
if (mvarFieldData[fieldName] == null)
{
mvarFieldData.Add(fieldName, value);
} else {
mvarFieldData[fieldName].FieldValue = value;
}
}
}
c# Converters(telerik) provide the below
public object Fields {
set {
if (mvarFieldData == null)
mvarFieldData = new FieldList();
if (mvarFieldData(fieldName) == null) {
mvarFieldData.Add(fieldName, value);
} else {
mvarFieldData(fieldName).FieldValue = value;
}
}
}
What is the scope of mVarFieldData, and what is it's exact type? It seems like it is a Collection of some sort.
The c# code above is not compilable as you are trying to mix a method syntax with a property syntax.
property:
public object Fields{ get; set{mvarFieldData = value;} }
Method:
public object Fields(string fieldname, object val){ mvarFieldData[fieldname] = val;}
By the looks of the decision making going on in the VB.Net property, I would think a public method may suit the situation better. I normally just use property's when a very minimum amount of validation is needed when setting a encapsulated type member.
Hope this helps.
You actually have a couple problems here.
Problem 1: Properties don't take arguments.
C# properties can't be passed an argument/parameter the way you're passing fieldname. There are a couple different ways you could solve this:
You can make an index property (used with myObject[fieldName] = blah rather than myObject.Fields[fieldName] = blah). Use the syntax public object this[string fieldName] to declare the property.
Since your property doesn't have a getter, you can just turn it into a single method with signature public void SetField(string fieldName, object value), called like so: myObject.SetField(fieldName, value).
You can expose mvarFieldData through a getter property: public Dictionary<?, ?> Fields { get { return mvarFieldData; } } which will let users access the dictionary (I assume that's what it is, based on usage) directly.
Problem 2: The key may not exist.
I'm not sure about dictionaries (or whatever similar structure mvarFieldData is) in VB.NET, but in C# there's a difference between a key whose value is null and a key that's not present in the structure.
var dict = new Dictionary<int, string>();
dict.Add(1, null);
if (dict[1] == null)
Console.WriteLine("This line will be displayed.");
if (dict[2] == null)
Console.WriteLine("The line above this one will throw an exception.");
In addition to your present code, you need a check for mvarFieldData.ContainsKey(fieldName) before you start checking the value associated with fieldName.
As the topic suggests I have some problems with PropertyInfo.SetValue. To get to the point, here is my example - I have created my own class and the main thing about it is the presentation object:
using System;
using System.Reflection;
namespace TestingSetValue
{
public class Link
{
private object presentationObject = null;
private string captionInternal = string.Empty;
public Link (string caption)
{
captionInternal = caption;
}
public string CaptionInternal
{
get { return captionInternal; }
set { captionInternal = value; }
}
public bool Visible
{
get
{
if (PresentationObject != null)
{
PropertyInfo pi = PresentationObject.GetType().GetProperty("Visible");
if (pi != null)
{
return Convert.ToBoolean(pi.GetValue(PresentationObject, null));
}
}
return true;
}
set
{
if (PresentationObject != null)
{
PropertyInfo pi = PresentationObject.GetType().GetProperty("Visible");
if (pi != null)
{
pi.SetValue(PresentationObject, (bool)value, null);
}
}
}
}
public object PresentationObject
{
get { return presentationObject; }
set { presentationObject = value; }
}
}
}
Then, I do this:
private void btnShowLink_Click(object sender, EventArgs e)
{
Link link = new Link("Here I am!");
this.contextMenu.Items.Clear();
this.contextMenu.Items.Add(link.CaptionInternal);
link.PresentationObject = this.contextMenu.Items[0];
link.Visible = true;
lblCurrentVisibility.Text = link.Visible.ToString();
}
Now, I can imagine this doesn't look too logical/ economical, but it shows the essence of my real problem. Namely, why doesn't the visibility of presentation object (and the value of link.Visible) change, after I call:
link.Visible = true;
I simply do not know what else to do to make this work... Any help is deeply appreciated.
To make things even more interesting, the property Enabled behaves as expected of it...
PropertyInfo pi = PresentationObject.GetType().GetProperty("Enabled");
Could it be related to the fact that Visible is actually a property of ToolStripDropDownItem base base object, whereas Enabled is 'direct' property of ToolStripDropDownItem ?
It would have been easier to figure this out if you said upfront what class this is but now we know it is ToolStripDropDownItem which we can infer means WinForms.
What you are seeing is an oddity with the ToolStripItem's Visible property. It's setter & getter are not tied directly together. MSDN says
"The Available property is different from the Visible property in that
Available indicates whether the ToolStripItem is shown, while Visible
indicates whether the ToolStripItem and its parent are shown. Setting
either Available or Visible to true or false sets the other property
to true or false."
In other words, you want to use the Available property instead of the Visible property
Check http://msdn.microsoft.com/en-us/library/system.web.ui.control.visible.aspx. Maybe this is causing your problem.
There is very important piece of info:
If this property is false, the server control is not rendered. You should take this into account when organizing the layout of your page. If a container control is not rendered, any controls that it contains will not be rendered even if you set the Visible property of an individual control to true. In that case, the individual control returns false for the Visible property even if you have explicitly set it to true. (That is, if the Visible property of the parent control is set to false, the child control inherits that setting and the setting takes precedence over any local setting.)
my class property has default value which will be serialize.
public class DeclaredValue
{
[XmlElement(ElementName = "Amount", DataType = "double", IsNullable = false), DefaultValue(999)]
public double Amount { get; set; }
[XmlElement(ElementName = "Reference2", DataType = "string", IsNullable = false), DefaultValue("")]
public string Reference2 { get; set; }
}
so we create instance of DeclaredValue class and provide value for Reference2 property and do not assign anything for Amount. so when we serialize the class DeclaredValue then no tag found for amount in my xml. i mention default value for amount "999" then why it does not work in serialization. i want that if do not assign anything for amount then amoun tag should be there in my xml with default value.
to do this what way i need to decorate the amount property that it always comes with default value in xml after serialization if user do not assign anything to this property.
please guide me what i need to change in the code to get my desired output.
Per the note on MSDN:
A DefaultValueAttribute will not cause
a member to be automatically
initialized with the attribute's
value. You must set the initial value
in your code.
Somewhat surprisingly the DefaultValue only regulates the writing of an object, members that are equal to their DefaultValue will not be written out.
You must still initialize members before or after loading yourself, for example in the constructor.
Let me thoroughly describe what is happening.
When XmlSerializer Deserialize() method is called, it creates a new object using a default constructor. It doesn't apply any DefaultValueAttributes to this object, I beleave, because of assumption that default ctor should "know better" how to initialize values by default. From this point of view - it is logical.
XmlSerializer doesn't serialize members which values are the same as marked by DefaultValue attribute. From some point of view such behavior is driven by logic too.
But when you do not initialize members in ctor and call deserialize method, XmlSerializer see no corresponding xml field, but it see that the field/property has DefaultValueAttribute, serializer just leave such value (according to the assumption that the default constructor knows better how to initialize a class "by defaults"). And you've got your zeros.
Solution
To initialize a class members by these DefaultValueAttributes (sometimes it is very handy to have this initialization values just in place) you can use such simple method:
public YourConstructor()
{
LoadDefaults();
}
public void LoadDefaults()
{
//Iterate through properties
foreach (var property in GetType().GetProperties())
{
//Iterate through attributes of this property
foreach (Attribute attr in property.GetCustomAttributes(true))
{
//does this property have [DefaultValueAttribute]?
if (attr is DefaultValueAttribute)
{
//So lets try to load default value to the property
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
try
{
//Is it an array?
if (property.PropertyType.IsArray)
{
//Use set value for arrays
property.SetValue(this, null, (object[])dv.Value);
}
else
{
//Use set value for.. not arrays
property.SetValue(this, dv.Value, null);
}
}
catch (Exception ex)
{
//eat it... Or maybe Debug.Writeline(ex);
}
}
}
}
}
This "public void LoadDefaults()", can be decorated as an Extension to object or use as some static method of a helper class.
As Henk Holterman mentionned, this attribut doesn't set the default value automatically. Its purpose is mostly to be used by visual designers to reset a property to its default value.
As others mentioned, the DefaultValue attribute doesn't initialize the property. You could use a simple loop to set all properties:
foreach (var property in GetType().GetProperties())
property.SetValue(this, ((DefaultValueAttribute)Attribute.GetCustomAttribute(
property, typeof(DefaultValueAttribute)))?.Value, null);
Even though ?.Value could return null, it works with non-nullable types, I tested this.
If only few of your properties have a default value, you should maybe only set the value if it is there.
If all properties should have a default value, remove the ? to get an error if you forgot one.
Most likely, arrays won't work, see MajesticRa's solution how to handle that.
I'm sending xml to another program, which expects boolean flags as "yes" or "no", rather than "true" or "false".
I have a class defined like:
[XmlRoot()]
public class Foo {
public bool Bar { get; set; }
}
When I serialize it, my output looks like this:
<Foo><Bar>true</Bar></Foo>
But I would like it to be this:
<Foo><Bar>yes</Bar></Foo>
Can I do this at the time of serialization? I would prefer not to have to resort to this:
[XmlRoot()]
public class Foo {
[XmlIgnore()]
public bool Bar { get; set; }
[XmlElement("Bar")]
public string BarXml { get { return (Bar) ? "yes" : "no"; } }
}
Note that I also want to be able to deserialize this data back again.
Ok, I've been looking into this some more. Here's what I've come up with:
// use this instead of a bool, and it will serialize to "yes" or "no"
// minimal example, not very robust
public struct YesNo : IXmlSerializable {
// we're just wrapping a bool
private bool Value;
// allow implicit casts to/from bool
public static implicit operator bool(YesNo yn) {
return yn.Value;
}
public static implicit operator YesNo(bool b) {
return new YesNo() {Value = b};
}
// implement IXmlSerializable
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader) {
Value = (reader.ReadElementContentAsString() == "yes");
}
public void WriteXml(XmlWriter writer) {
writer.WriteString((Value) ? "yes" : "no");
}
}
Then I change my Foo class to this:
[XmlRoot()]
public class Foo {
public YesNo Bar { get; set; }
}
Note that because YesNo is implicitly castable to bool (and vice versa), you can still do this:
Foo foo = new Foo() { Bar = true; };
if ( foo.Bar ) {
// ... etc
In other words, you can treat it like a bool.
And w00t! It serializes to this:
<Foo><Bar>yes</Bar></Foo>
It also deserializes correctly.
There is probably some way to get my XmlSerializer to automatically cast any bools it encounters to YesNos as it goes - but I haven't found it yet. Anyone?
Very simple. Use a surrogate property. Apply XmlIgnore on the actual property. The surrogate is a string, and must use the XmlElement attribute that takes a element-name override. Specify the name of the actual property in the override. The surrogate property serializes differently based on the value of the actual property. You must also provide a setter for the Surrogate, and the setter should set the actual property appropriately, for whatever value it serialized. In other words it needs to go both ways.
Snip:
public class SomeType
{
[XmlElement]
public int IntValue;
[XmlIgnore]
public bool Value;
[XmlElement("Value")]
public string Value_Surrogate {
get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; }
set { Value= (value=="Yes, definitely!"); }
}
}
click here for full compilable source example.
Making a bool value serialize as "yes" or "no" changes the data type from being a boolean at all. Instead, can you add a separate property which evaluates a boolean and returns "yes" or "no" as appropriate for it's data type? Maybe you could even force "yes" or "no" by making the return type be an enum which only specifies those values.
public YesOrNo DoYouLoveIt
{
get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; }
}
That might be overkill, but might answer your need. The only reason I bring up an enum for such a simple value is you'd be restricting the values vs. allowing any string.
I use the property method, but instead of checking to see if the string is equal to yes or no, I prefer to check if the string starts with (case insensitive) "YT1". This allows the file to contain true, True, t, T, y, Y, yes, Yes, 1, etc. all which will evaluate to true. While I can specify that false is false, False, f, F, n, N, no, No, 0, etc., anything that doesn't match the true still evaluates to false.
Your property example is probably the simplest way you could do it. If it helps, I believe you don't need to make it a public property, since the attribute implements ISerializable on the class behind your back. To enable deserialization, you should be able to just implement set { Bar = value == "yes"; }
#Blorgbeard:
If you have more then one of these YesNo classes in an object class,
make sure to read the entire element.
public void ReadXml(XmlReader reader)
{
string element = reader.ReadOuterXml();
int startIndex = element.IndexOf('>') + 1;
int length = element.LastIndexOf('<') - startIndex;
string text = (element.Substring(startIndex, length).ToLowerInvariant();
Value = (text == "yes");
}
Otherwise this might cause problems.
The ReadXml method must reconstitute your object using the information that was written by the WriteXml method.
When this method is called, the reader is positioned at the start of the element that wraps the information for your type. That is, just
before the start tag that indicates the beginning of a serialized
object. When this method returns, it must have read the entire element
from beginning to end, including all of its contents. Unlike the
WriteXml method, the framework does not handle the wrapper element
automatically. Your implementation must do so. Failing to observe
these positioning rules may cause code to generate unexpected runtime
exceptions or corrupt data.
What you're needing to do sounds more like a display issue. If your application allows, you will be better off keeping the data type as a boolean and displaying Yes/No in your user interface.