2

I have this class:

using Sierra.Collections;

namespace Sierra
{
    public sealed class Worlds : SafeKeyedCollection<byte, World>
    {
        public Worlds() : base() { }

        protected override byte GetKeyForItem(World item)
        {
            return item.Id;
        }
    }
}

World class:

using Sierra.Collections;

namespace Sierra
{
    public sealed class World : SafeKeyedCollection<byte, Channel>
    {
        public byte Id { get; set; }
        public string Name { get; set; }
        public ushort Port { get; set; }
        public ushort ShopPort { get; set; }
        public ushort MonsterLifePort { get; set; }
        public ushort TalkPort { get; set; }
        public byte Ribbon { get; set; }
        public byte Channels { get; set; }
        public string EventMessage { get; set; }
        public string ScrollingHeader { get; set; }
        public bool AllowMultiLeveling { get; set; }
        public int DefaultCreationSlots { get; set; }
        public bool DisableCharacterCreation { get; set; }

        public World() : base() { }

        protected override byte GetKeyForItem(Channel item)
        {
            return item.Id;
        }
    }
}

When I a Worlds object, it doesn't serialize it. The list appears to be empty:

public Worlds Worlds { get; set; }

It outputs:

"Worlds": [
    []
]

Why's that? Can't you serialize a class that inherits from another class?

SafeKeyedCollection

    using System;
using System.Collections;
using System.Collections.Generic;

namespace Sierra.Collections
{
    /// <summary>
    /// Thread safe NumericalKeyedCollection class - Patel
    /// Soooooooooooooo fire
    /// </summary>
    public abstract class SafeKeyedCollection<TKey, TItem> : IEnumerable<TItem>
    {
        private Object m_Lock;
        private Dictionary<TKey, TItem> m_Inner;

        public SafeKeyedCollection()
        {
            m_Lock = new object();
            m_Inner = new Dictionary<TKey, TItem>();
        }

        public int Count
        {
            get
            {
                lock (m_Lock)
                {
                    return m_Inner.Count;
                }
            }
        }

        public TItem this[TKey key]
        {
            get
            {
                lock (m_Lock)
                {
                    TItem ret;

                    m_Inner.TryGetValue(key, out ret);

                    return ret;
                }
            }
        }

        public void Add(TItem item)
        {
            this.InsertItem(item);
        }

        public void Remove(TItem item)
        {
            this.RemoveItem(item);
        }

        protected virtual void InsertItem(TItem item)
        {
            lock (m_Lock)
            {
                TKey key = GetKeyForItem(item);
                m_Inner.Add(key, item);

            }
        }
        protected virtual void RemoveItem(TItem item)
        {
            lock (m_Lock)
            {
                TKey key = GetKeyForItem(item);
                m_Inner.Remove(key);
            }
        }
        public void Clear()
        {
            lock (m_Lock)
            {
                m_Inner.Clear();
            }
        }
        public bool Contains(TKey key)
        {
            lock (m_Lock)
            {
                return m_Inner.ContainsKey(key);
            }
        }
        public bool Contains(TItem value)
        {
            lock (m_Lock)
            {
                return m_Inner.ContainsValue(value);
            }
        }

        protected abstract TKey GetKeyForItem(TItem item);

        protected virtual void ClearItems() { }

        public IEnumerator<TItem> GetEnumerator()
        {
            return new SafeEnumerator<TItem>(() => m_Inner.Values.GetEnumerator(), m_Lock);
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

Thanks

Gilbert Williams
  • 778
  • 1
  • 4
  • 17

1 Answers1

1

The basic problem is that you are trying to serialize a collection (here SafeKeyedCollection<TKey, TItem> : IEnumerable<TItem>) that has been subclassed to contain custom properties. This cannot be done, because the JSON standard defines only two types of container:

  1. Objects with named key/value properties: {"a": 1, "b" : "two" }
  2. Arrays of elements: [1, 2, 3, {"a": 1}]

There is no container that can have both named properties and an arbitrary number of unnamed elements -- either the items or the properties must take precedence in serialization. And in fact all JSON serializers choose to serialize anything implementing IEnumerable as an array by default, omitting the properties. This is what you are seeing. You can in general serialize a class that inherits from another class, just not settable properties of subclassed collections.

How to deal with this? Microsoft's Guidelines for Collections state

AVOID implementing collection interfaces on types with complex APIs unrelated to the concept of a collection.

So you might consider changing your data model (which will also cause problems with XmlSerializer) to have the collections be contained in some higher object, rather than subclassed.

If you wish to keep your current data model, then your available workarounds depend on the serializer you are using:

dbc
  • 80,875
  • 15
  • 141
  • 235