Pedro Henrique
Pedro Henrique

Reputation: 782

Serialization when inheriting from Dictionary

I'm using System.Web.Script.Serialization.JavaScriptSerializer to serialize / deserialize a class that extends Dictionary.

The problem is, my custom properties are not being serialized. Here is my class:

public class Test : Dictionary<string, object> {
    public ushort Id { get; set; }
    public string Name { get; set; }
}

And my code:

var jss = new JavaScriptSerializer();

var test = new Test {
    Id = 123,
    Name = "test"
};

var json = jss.Serialize(test);

The result in json is an empty json {}

I do not want to depend on Newtonsoft or JSON.Net or any other library.

ADDITIONAL INFO

I just noticed some, hm, peculiarities, when using both dynamic and object:

That can cause casting exceptions in a class using property indexer (as suggested in the accepted answer), for example:

public class Test : Dictionary<string, dynamic> {
    public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}

The Id property getter will try to implicitly convert int to ushort, which will fail.

ADDITIONAL INFO 2

I just found out so many weird behaviors with Newtonsoft:

I added these attributes to solve the 'long to ushort' problem:

[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
    [JsonProperty]
    public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}

The above works! But when the property is a reference type:

[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
    [JsonProperty]
    public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
    [JsonProperty]
    public Test Child { get => this[nameof(Child)]; set => this[nameof(Child)] = value; }
}

It tries to get the property before serializing it, resulting in a 'key not found exception'. I can't see why it tries to get the property only when it's a reference type, seems like a bug to me...

So you have to do something like this:

public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }

Upvotes: 0

Views: 877

Answers (1)

Jason W
Jason W

Reputation: 13209

Just to summarize the comments:

To use composition, you would just need to modify your test object like this:

public class Test
{
    public ushort Id { get; set; }
    public string Name { get; set; }
    public Dictionary<string, object> Items { get; set; } = new Dictionary<string, object> {};
}

Then the following code would work fine:

var jss = new JavaScriptSerializer();
var test = new Test
{
    Id = 123,
    Name = "test",
};
test.Items.Add("A", 1);
var json = jss.Serialize(test);

The output would just be:

{"Id":123,"Name":"test","Items":{"A":1}}

UPDATE: Property Indexer

You could add a default indexer to your class so that the following code would work:

test["A"] = 1;
var result = test["A"];

Here is the code to add for the default indexer:

public object this[string key]
{
    get { return this.Items[key]; }
    set { this.Items[key] = value; }
}

You could extend this into implementing IDictionary I suppose, but I imagine just working with the composition should be easiest.

Upvotes: 0

Related Questions