0

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.

  1. 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).

  2. 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.

dbc
  • 104,963
  • 20
  • 228
  • 340
user2079828
  • 645
  • 1
  • 8
  • 31
  • *I would like the easiest way to serialize the 'isVisible' field to 'true' every single time it is serialized* -- you mean you want `true` to appear in the serialized XML, or you want the actual field value in the `MapItem` class to be modified to be `true` during serialization? – dbc Oct 26 '17 at 19:19
  • If you always want it to be `true`, why do you have an option of it being false? Do you even really need to serialize it? Can you just default it to true and ignore `IsVisible` with the `IgnoreDataMemberAttribute`? – TyCobb Oct 26 '17 at 19:20
  • @dbc Yes, I want the XML to always have true OR I want the value inside MapItem to be 'true' ONLY when deserializing. 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. – user2079828 Oct 26 '17 at 19:35
  • @TyCobb As stated above. While running the application, these items maybe visible or not visible. Only on load do I need to set them all to visible. But, I figured the less time consuming way to do this was to save them out as visible instead of load them in that way. – user2079828 Oct 26 '17 at 19:38
  • The basic problem here is that [`DataContractSerializer` doesn't call your constructor](https://stackoverflow.com/q/1076730) so you have no convenient way to initialize a read-only `bool` field to `true`. Would you be willing to flip the meaning of the field from `isVisible` to `isNotVisible`? In that case the default value would be the correct value. – dbc Oct 26 '17 at 19:38
  • @dbc Interesting solution. Probably not, though. I think readability would suffer and other devs would not like it. – user2079828 Oct 26 '17 at 19:40

1 Answers1

1

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:

  1. 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; } }
    }
    
  2. 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);
        }
    }
    
  3. 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.

dbc
  • 104,963
  • 20
  • 228
  • 340