简体   繁体   中英

Using JSON.NET with OrderedDictionary

I have an OrderedDictionary with int keys and System.Drawing.Rectangle values. JSON.NET won't serialize the OrderedDictionary ...it returns an empty object. I wrote a custom converter, but I wondered if there was an easier way. Thinking that JSON.NET might use the presence of a typed enumerator as the trigger to use its built-in code for serializing and deserializing a Dictionary<TKey, TValue> I tried this:

class Program
{
    static void Main(string[] args)
    {
        var test = new OrderedDictionary<int, Rectangle>();
        test.Add(1, new Rectangle(0, 0, 50, 50));
        test.Add(42, new Rectangle(1, 1, 1, 1));

        string s = JsonConvert.SerializeObject(test);
        var deserialized = JsonConvert.DeserializeObject<OrderedDictionary<int, Rectangle>>(s);

        var someRect = deserialized[(object)1]; // someRect is null
        var someOtherRect = (Rectangle)deserialized["1"]; // InvalidCastException
    }
}

public class OrderedDictionary<TKey, TValue> : OrderedDictionary, IEnumerable<KeyValuePair<TKey, TValue>>
{
    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
    {
        foreach (TKey key in Keys)
        {
            yield return new KeyValuePair<TKey, TValue>(key, (TValue)this[key]);
        }
    }
}

Serialization works perfectly. However, when I deserialize, the keys in the dictionary become strings and the Rectangle s are JObject s that can't be cast to Rectangle . Is there something I can add to my OrderedDictionary<> class that will allow for proper deserialization with JSON.NET? Thanks.

Your problem is that, although you've added an enumerator, things like the indexer cannot be overridden. So what you're getting is the default implementation of the non-generic OrderedDictionary which doesn't give you a typed result.

So, instead of inheriting, you need a facade that fully implements the generic interface.

You'll need to verify my class (I just made the test work). I've also cheated with the Keys and Values properties (they're not often used) and some of the other ICollection methods. Just lazy :)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using Newtonsoft.Json;
using Xunit;

namespace XUnitTestProject1
{
    public class UnitTest1
    {
        [Fact]
        public void TestJsonRectange()
        {
            var test = new OrderedDictionary<int, Rectangle>();
            test.Add(1, new Rectangle(0, 0, 50, 50));
            test.Add(42, new Rectangle(1, 1, 1, 1));
            string json = JsonConvert.SerializeObject(test);

            var deserialized = JsonConvert.DeserializeObject<OrderedDictionary<int, Rectangle>>(json);

            object someRect = deserialized[1];
            Assert.NotNull(someRect);
            Assert.True(someRect is Rectangle);
        }
        [Fact]
        public void TestJsonString()
        {
            var test = new OrderedDictionary<string, string>();
            test.Add("1", "11");
            test.Add("42", "4242");
            string json = JsonConvert.SerializeObject(test);

            var deserialized = JsonConvert.DeserializeObject<OrderedDictionary<string, string>>(json);

            object something = deserialized["1"];
            Assert.NotNull(something);
            Assert.True(something is string);
        }

        public class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
        {
            private readonly OrderedDictionary dic = new OrderedDictionary();

            public TValue this[TKey key] { get { return (TValue)dic[key]; } set { dic[key] = value; } }

            public void Add(KeyValuePair<TKey, TValue> item)
            {
                dic.Add(item.Key, item.Value);
            }
            public void Add(TKey key, TValue value)
            {
                dic.Add(key, value);
            }

            public void Clear() { dic.Clear(); }


            public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { }


            public int Count { get { return dic.Count; } }
            public bool IsReadOnly { get { return false; } }

            public bool Contains(TKey key) { return dic.Contains(key); }
            public bool ContainsKey(TKey key) { return dic.Contains(key); }

            public bool Remove(TKey key) { dic.Remove(key); return true; }

            public bool TryGetValue(TKey key, out TValue value) { value = default(TValue); return false; }

            bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
            {
                throw new NotImplementedException();
            }
            bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { return false; }

            public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
            {
                foreach (DictionaryEntry entry in dic)
                    yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            private static readonly TKey[] keys = new TKey[0];
            private static readonly TValue[] values = new TValue[0];

            ICollection<TKey> IDictionary<TKey, TValue>.Keys { get { return keys; } }
            ICollection<TValue> IDictionary<TKey, TValue>.Values { get { return values; } }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM