4

I am trying to serialize a class that is made up of many variables all inheriting from IMyDataType.

public abstract class IMyDataType<T>
{
    protected virtual T Data { get; set; }
    public abstract String ToString(String args = null);
    public abstract Boolean SetValue(T newValue);
    public abstract Boolean CheckValue(T newValue);
    public abstract T GetValue();
}

For instance I might have a class MyInteger

public class MyInteger : IMyDataType<int>
{
    public int Min { get; protected set; }
    public int Max { get; protected set; }
    protected override int Data { get; set; }

    public MyInteger(int value)
    {
        Min = int.MinValue;
        Max = int.MaxValue;
        if (!SetValue(value))
            Data = 0;
    }

    public MyInteger(int min, int value, int max)
    {
        Min = min;
        Max = max;
        if (!SetValue(value))
            Data = 0;
    }

    public override Boolean SetValue(int newVal)
    {
        if (newVal >= Min && newVal <= Max)
        {
            Data = newVal;
            return true;
        }
        return false;
    }

    public override Boolean CheckValue(int newVal)
    {
        return (newVal >= Min && newVal <= Max);
    }

    public override int GetValue() { return Data; }
    public override String ToString(String args = null) { return Data.ToString(args); }
}

Whenever I try to serialize a subclass of IMyDataType the variable T Data is never serialized, though Min and Max are. What do I need to do to get T Data to be serialized?

EDIT: Based on DotNetom's answer I changed the access modifier of T Data to public, which did allow it to be serialized. Though, there are reasons for it being protected. Is there any other means by which I could serialize it while keeping its access modifier intact?

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
KDecker
  • 6,928
  • 8
  • 40
  • 81

2 Answers2

5

You could simply create an alternate private property getter for Data and mark it with a [JsonProperty] attribute. This will allow Json.Net to "see" it and serialize it without needing to alter the access modifier of the original property.

public abstract class IMyDataType<T>
{
    protected virtual T Data { get; set; }
    ...

    [JsonProperty("Value")]
    private T AlternateData
    {
        get { return Data; }
    }
}

Fiddle: https://dotnetfiddle.net/0JMZzX

(Note: In theory, you could just mark the protected Data property itself with the [JsonProperty] attribute instead of creating an alternate property getter. However, this only seems to work if the base property is not overridden in the subclass. Once you override it, the attribute no longer works for some reason -- even if you move the attribute to the subclass or place it in both the base class and subclass. I'm not sure whether this is a bug in Json.Net or intentional. Using an alternate getter always seems to work though.)

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • Another reason you wouldn't want to put the attribute directly on the base class as the go to method of solving the problem is it potentially violated SRP and DI. Example: If you have a Configuration base class that provides common configuration functions and properties for different classes then tagging the base class so the JsonConfiguration class can access that one property ties the base class to Json, which defeats the point of the base class. Also makes it awkward when someone peeking into the attributes via reflection sees a Json thing in a Sql class when reflecting. – XerShade Jan 22 '22 at 14:02
4

By default, Json.NET serializes only properties with public getters (see docs). Your property Data is protected:

protected virtual T Data { get; set; }

In order for it to be serialized you should change it to public:

public virtual T Data { get; set; }

If changing access to public is not an option, there are ways to serialize non public items as well by implementing custom contract resolvers. Samples on how to use them can be found in answers to this question on SO - JSON.Net: Force serialization of all private fields and all fields in sub-classes

kmote
  • 16,095
  • 11
  • 68
  • 91
dotnetom
  • 24,551
  • 9
  • 51
  • 54
  • 1
    This does work DotNetom. Though I have reasons for `T Data` to be protected. Any idea how to keep the access modifier intact? – KDecker Oct 05 '15 at 17:22
  • @KDecker I haven't done this myself, but seems you can impement custom contract resolver to serialize not only public properties. I updated the answer with link which can help you – dotnetom Oct 05 '15 at 17:27
  • @dotnetom - side note: consider just using link to SO questions - it gets auto-converted to title which frequently is exactly what is needed (plus way more readable that "see this"). I've made an edit to the post to show - feel free to rollback, – Alexei Levenkov Oct 05 '15 at 17:49
  • @AlexeiLevenkov Thanks for the tip, I did not know SO links are automatically converted to titles – dotnetom Oct 05 '15 at 17:51
  • This is the "correct" answer, but I am going to end up implementing Brian's answer below because I do not need everything a custom contract would offer. – KDecker Oct 06 '15 at 12:49