6

Given:

public class MyClass : INotifyPropertyChanged
{
    public List<string> _TestFire = new List<string>();

    string _StringProp;
    public string StringProp
    {
        get
        {
            return _StringProp;
        }
        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;
                RaisePropertyChanged("StringProp");
                _TestFire.Add("fired " + DateTime.Now);
            }
        }
    }
}

When you serialize and then deserialize this class, it fires the RaisePropertyChanged event, which is undesirable. Is it possible to prevent this event from being fired when the class gets deserialized?

var MyclassInstance = new MyClass() { StringProp = "None" };

MyclassInstance._TestFire.Clear(); // Clear property change history

var serobj = JsonConvert.SerializeObject();

var newitem = JsonConvert.DeserializeObject<MyClass>(serobj);

// newitem._TestFire.Count == 1, the set method was executed

Is there a way to get a bool value if the class is being deserialized? So then I could do:

        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;

                if (!Deserializing)
                {
                    RaisePropertyChanged("StringProp");
                    _TestFire.Add("fired " + DateTime.Now);
                }
            }
        }
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
Watson
  • 1,385
  • 1
  • 15
  • 36

1 Answers1

7

Yes, you can do what you want by implementing the OnDeserializing and OnDeserialized serialization callback methods in your class. In the OnDeserializing method, set your private _isDeserializing variable to true, and in OnDeserialized set it back to false. I would recommend doing the _isDeserializing check inside the RaisePropertyChanged method so you don't have duplicate code inside every property.

So you would end up with something like this:

public class MyClass : INotifyPropertyChanged
{
    public List<string> _TestFire = new List<string>();

    string _StringProp;
    public string StringProp
    {
        get
        {
            return _StringProp;
        }
        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;
                RaisePropertyChanged("StringProp");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        // don't raise the event if the property is being changed due to deserialization    
        if (_isDeserializing) return;

        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        _TestFire.Add(propertyName + " was fired " + DateTime.Now);
    }

    bool _isDeserializing = false;

    [OnDeserializing]
    internal void OnDeserializingMethod(StreamingContext context)
    {
        _isDeserializing = true;
    }

    [OnDeserialized]
    internal void OnDeserializedMethod(StreamingContext context)
    {
        _isDeserializing = false;
    }
}

Working demo: https://dotnetfiddle.net/QkOcF4


Another way you could solve this problem is to mark your public properties with [JsonIgnore] and then mark the corresponding backing fields with [JsonProperty], specifying the property name to use in the JSON. This would allow Json.Net to set the backing fields directly and not execute any of the mutator logic.

public class MyClass : INotifyPropertyChanged
{
    public List<string> _TestFire = new List<string>();

    [JsonProperty("StringProp")]
    string _StringProp;

    [JsonIgnore]
    public string StringProp
    {
        get
        {
            return _StringProp;
        }
        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;
                RaisePropertyChanged("StringProp");
                _TestFire.Add("StringProp was fired " + DateTime.Now);
            }
        }
    }

    ...
}

Working demo: https://dotnetfiddle.net/jc7wDu

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • 1
    That's extremely helpful. Thank you! – Watson Apr 12 '19 at 01:10
  • Agree, can't believe I've been working with JSON for so long, and not hit this problem. @BrianRogers thanks – johnc Dec 08 '20 at 00:04
  • I liked the idea of putting JsonProperty attribute on the backing field, this is brilliance. Just for double checking as this question is about deserialization. Is it ok to use this trick on serialization also. – Jawad Sabir Jan 17 '22 at 05:45
  • @JawadSabir Yes, it should work. Give it a try. – Brian Rogers Jan 17 '22 at 07:09
  • Useful for sure. I had to modify the first idea two ways: I could see via the default constructor on my class was running *before* the OnDeserializing method. I know that's not supposed to happen, but it is. So set _isDeserializing=true at the beginning of the default .ctor too. Second: needed to set [JsonIgnore] on the field _isDeserializing. – user3486991 Jun 13 '22 at 00:46