2015-06-16 6 views
2

У меня есть этот класс:C# - Json.NET - сериализация с наследованием

using Sierra.Collections; 

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

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

Мировой класс:

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; 
     } 
    } 
} 

Когда я Worlds объект, он не сериализовать. Список выглядит пустым:

public Worlds Worlds { get; set; } 

Он выводит:

"Worlds": [ 
    [] 
] 

Почему? Разве вы не можете сериализовать класс, который наследуется от другого класса?

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(); 
     } 
    } 
} 

Благодаря

ответ

1

Основная проблема заключается в том, что вы пытаетесь сериализовать коллекцию (здесь SafeKeyedCollection<TKey, TItem> : IEnumerable<TItem>), который был подклассы содержать пользовательские свойства. Это не может быть сделано, потому что JSON standard определяет только два типа контейнера:

  1. объекты с именованными свойствами ключ/значение: {"a": 1, "b" : "two" }
  2. Массивы элементов: [1, 2, 3, {"a": 1}]

Там нет контейнера, который может имеют и именованные свойства и произвольное количество неназванных элементов - либо элементы, либо свойства должны иметь приоритет в сериализации. И на самом деле все сериализаторы JSON решили сериализовать что-либо, реализующее IEnumerable как массив по умолчанию, опустив свойства. Это то, что вы видите. Вы можете вообще сериализовать класс, который наследуется от другого класса, а не настраиваемые свойства подклассовых коллекций.

Как с этим бороться? Microsoft Guidelines for Collections состояние

AVOID реализует интерфейсы сбора данных по типам со сложными API, не связанными с концепцией коллекции.

Таким образом, вы могли бы рассмотреть вопрос об изменении модели данных (которая также может вызвать проблемы с XmlSerializer) иметь в коллекции содержится в некотором более высоком объекте, а не на подклассы.

Если вы хотите сохранить текущую модель данных, то ваши имеющиеся обходные пути зависят от сериализатора вы используете:

  • С Json.NET, вы могли бы отметить свой класс с JsonObjectAttribute затем создать некоторый частный получить/установить прокси свойство для сериализации элементов.
  • Также с Json.NET вы можете создать собственный конвертер для своих коллекций. См. JSON.NET with custom collection (with collection properties).
  • Если вы используете DataContractJsonSerializer, вы можете использовать data contract surrogate, чтобы сопоставить свою коллекцию с классом, содержащим элементы коллекции во вложенной коллекции.