Reputation: 655
The below class is a contrived example. I would like the easiest way to serialize the 'isVisible' field to 'true' every single time it is serialized...regardless of what it is set to.
In the real code...this is a much larger object and has a few different constructors that are called from different points.
Right now, I can think of two ways.
Create an 'OnSerializing()' method that used reflection to set the property to 'true' during serialization. (I'm not actually sure if this will work...but it seems like it would).
In the method that initiates the serialization of the class...create all new items that have the isVisible as 'true' before serializing.
[DataContract]
public class MapItem
{
public MapItem(bool isVisible)
{
this.isVisible = isVisible;
}
[DataMember]
private readonly bool isVisible;
public bool IsVisible => isVisible;
}
Why do I need this? Basically, I need to load in these items and have them always be visible directly after a load. While running the application...the MapItems may become visible/invisible. Thus I want the XML to always have <isVisible>true</isVisible>
OR I want the value inside MapItem to be 'true' ONLY when deserializing.
Upvotes: 0
Views: 969
Reputation: 116981
You would like the field private readonly bool isVisible;
to be initialized to true
during deserialization. Unfortunately, DataContractSerializer
doesn't call any constructor of your class so there is no obvious, easy way to do this.
Your proposed solution -- of always emitting <isVisible>true</isVisible>
during serialization and taking advantage of the data contract serializer's ability to deserialize read-only field values -- is less than ideal because a badly crafted XML file, e.g. one where <isVisible>
is false
or even missing, could introduce unexpected behavior into your application.
Instead, consider the following alternatives that do not require serializing the value of isVisible
:
Modify the name and semantics of the read-only field so that the default value is the correct, desired value. In this case you would need to replace the current isVisible
with private readonly bool isNotVisible;
:
[DataContract]
public class MapItem
{
// Do not mark with [DataContract] as deserialized instances should always have the default value
private readonly bool isNotVisible;
public MapItem(bool isVisible)
{
this.isNotVisible = !isVisible;
}
public bool IsVisible { get { return !isNotVisible; } }
}
Set the field value in an [OnDeserializing]
callback using reflection:
[DataContract]
public class MapItem
{
public MapItem(bool isVisible)
{
this.isVisible = isVisible;
}
// Do not mark with [DataContract] as deserialized instances should always have the value
// set in the OnDeserializing() callback.
private readonly bool isVisible;
public bool IsVisible { get { return isVisible; } }
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{
typeof(MapItem)
.GetField(nameof(isVisible), BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(this, true);
}
}
Finally, you could implement ISerializable
as this is supported by the data contract serializer, and initialize isVisible
to true
in the streaming constructor:
[Serializable]
public class MapItem : ISerializable
{
public MapItem(bool isVisible)
{
this.isVisible = isVisible;
}
private readonly bool isVisible;
public bool IsVisible { get { return isVisible; } }
#region ISerializable Members
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
#endregion
protected MapItem(SerializationInfo info, StreamingContext context)
{
this.isVisible = true;
}
}
However this is not recommended as you now would need to serialize all of your class's members manually.
Upvotes: 1