【问题标题】:fastJSON Deserialization ListfastJSON 反序列化列表
【发布时间】:2016-04-14 22:55:50
【问题描述】:

有点像我上一个问题的基础。我正在尝试保存一个蓝图,它只是游戏对象/实体的一堆设置。我现在将组件(及其设置)存储为一个 List(IEntityComponent 是任何组件的接口),并包装在一个名为 ComponentTable 的类中。我只想序列化列表,所有私有的东西都没有序列化,只是为了更快的查找(以内存为代价)。这可以正确序列化,甚至反序列化没有任何错误,但我注意到 componentTable 没有正确反序列化。

它创建了一个 ComponentTable 的实例,但实际上从未向它添加值。所以不是一个包含 CameraComponent、VelocityComponent 和 InputComponent 的 Component 表,它只是一个空的 ComponentTable。

{
 "$types" : {
  "ECS.Framework.Collections.Blueprint, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" : "1",
  "ECS.Features.Core.CameraComponent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" : "2",
  "ECS.Features.Core.VelocityComponent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" : "3",
  "InputComponent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" : "4"
 },
 "$type" : "1",
 "Components" : [
  {
     "$type" : "2",
     "Tag" : "MainCamera",
     "Test" : "0, 0, 0",
     "BackgroundColour" : "0, 0, 1, 1",
     "ViewportRect" : "10, 10 : 10, 10",
     "Orthographic" : false,
     "FieldOfView" : 60,
     "OrthoSize" : 5,
     "Depth" : 0,
     "OcclusionCulling" : true,
     "HDR" : false,
     "Enabled" : true
  },
  {
     "$type" : "3",
     "Enabled" : true,
     "CurrentVelocity" : "0, 0, 0"
  },
  {
     "$type" : "4",
     "TEST" : 0,
     "Enabled" : true
  }
 ],
 "Children" : [

 ],
 "Parent" : ""
}

这就是它的保存方式,所以看起来它正在正确保存。我只控制向量、矩形和颜色的序列化/序列化,因为任何统一值类型都会导致错误。

我相信它可以正确序列化,但由于某种原因它没有反序列化到 componentTable。有谁知道 fastJSON 是否存在这种继承问题(使类继承自 List

理想情况下,我会将它作为 Dictionary 继承,但 fastJSON 不会序列化 Type,只是将其保存为 'System.Mono' 然后在序列化时会导致错误。

编辑:这里是蓝图和组件表类

public sealed class Blueprint 
{
    public ComponentTable Components { get; private set; }

    public List<string> Children { get; set; }

    public string Parent { get; set; }

    public Blueprint()
    {
        Components = new ComponentTable();

        Children = new List<string>();
        Parent = "";
    }

    public Blueprint(Blueprint _blueprint)
    {
        Children = new List<string>(_blueprint.Children);

        Parent = _blueprint.Parent;
    }
}


public class ComponentTable : List<IEntityComponent>
{
    private Dictionary<Type, IEntityComponent> Components { get; set; }

    #region Constructors

    public ComponentTable()
    {
        Components = new Dictionary<Type, IEntityComponent>();
    }

    #endregion

    #region Base Function Overrides

    public void Add(Type _type)
    {
        if (Components.ContainsKey(_type))
            return;

        InternalAdd(_type, (IEntityComponent)Activator.CreateInstance(_type));
    }
    public new void Add(IEntityComponent _component)
    {
        InternalAdd(_component.GetType(), _component);
    }
    public void Add<T>() where T : IEntityComponent
    {
        Add(typeof(T));
    }
    private void InternalAdd(Type _type, IEntityComponent _component)
    {
        if (Components.ContainsKey(_type))
            throw new InvalidOperationException("Component already contained");

        Components.Add(_type, _component);
        base.Add(_component);
    }

    public bool Remove(Type _type)
    {
        if (Components.ContainsKey(_type))
            return InternalRemove(_type, Components[_type]);
        return false;
    }
    public new bool Remove(IEntityComponent _component)
    {
        return InternalRemove(_component.GetType(), _component);
    }
    public bool Remove<T>() where T : IEntityComponent
    {
        return Remove(typeof(T));
    }
    private bool InternalRemove(Type _type, IEntityComponent _component)
    {
        if (!Components.ContainsKey(_type))
            return false;

        Components.Remove(_type);
        return base.Remove(_component);
    }

    public IEntityComponent Get(Type _type)
    {
        if (Contains(_type))
            return Components[_type];
        return null;
    }
    public T Get<T>() where T : IEntityComponent
    {
        return (T)Get(typeof(T));
    }

    public bool TryGetValue(Type _type, out IEntityComponent _component)
    {
        return Components.TryGetValue(_type, out _component);
    }
    public bool TryGetValue<T>(out IEntityComponent _component) where T : IEntityComponent
    {
        return TryGetValue(typeof(T), out _component);
    }

    public bool Contains(Type _type)
    {
        return Components.ContainsKey(_type);
    }
    public new bool Contains(IEntityComponent _component)
    {
        return Contains(_component.GetType());
    }
    public bool Contains<T>() where T : IEntityComponent
    {
        return Contains(typeof(T));
    }

    #endregion

}

【问题讨论】:

    标签: c# json serialization unity3d fastjson


    【解决方案1】:

    我在 Microsoft .Net 上测试了一下,发现了以下问题:

    1. fastJSON 不会反序列化 Components 属性,除非它具有公共设置器:

      public sealed class Blueprint 
      {
          public ComponentTable Components { get; set; }
      

      似乎没有任何类型的配置选项可以解决此问题。从Reflection.cs 可以看到创建setter 委托的方法返回null 如果setter 不是公共的:

      internal static GenericSetter CreateSetMethod(Type type, PropertyInfo propertyInfo)
      {
          MethodInfo setMethod = propertyInfo.GetSetMethod();
          if (setMethod == null)
              return null;
      
    2. fastJSON 确实似乎无法反序列化 List&lt;T&gt; 的子类——或任何其他非数组集合类——不是通用的。在deserializer 里面有如下检查:

      if (pi.IsGenericType && pi.IsValueType == false && v is List<object>)
          oset = CreateGenericList((List<object>)v, pi.pt, pi.bt, globaltypes);
      

      如您所见,它检查目标类型是否为泛型,而不是目标类型或其基类型之一是否为泛型。

      您可以通过将 ComponentTable 设为通用来解决此问题:

      public class ComponentTable<TEntityComponent> : List<TEntityComponent> where TEntityComponent : IEntityComponent
      {
          private Dictionary<Type, TEntityComponent> Components { get; set; }
      
          #region Constructors
      
          public ComponentTable()
          {
              Components = new Dictionary<Type, TEntityComponent>();
          }
      
          #endregion
      
          #region Base Function Overrides
      
          public void Add(Type _type)
          {
              if (Components.ContainsKey(_type))
                  return;
      
              InternalAdd(_type, (TEntityComponent)Activator.CreateInstance(_type));
          }
          public new void Add(TEntityComponent _component)
          {
              InternalAdd(_component.GetType(), _component);
          }
          public void Add<T>() where T : IEntityComponent
          {
              Add(typeof(T));
          }
          private void InternalAdd(Type _type, TEntityComponent _component)
          {
              if (Components.ContainsKey(_type))
                  throw new InvalidOperationException("Component already contained");
      
              Components.Add(_type, _component);
              base.Add(_component);
          }
      
          public bool Remove(Type _type)
          {
              if (Components.ContainsKey(_type))
                  return InternalRemove(_type, Components[_type]);
              return false;
          }
          public new bool Remove(TEntityComponent _component)
          {
              return InternalRemove(_component.GetType(), _component);
          }
          public bool Remove<T>() where T : IEntityComponent
          {
              return Remove(typeof(T));
          }
          private bool InternalRemove(Type _type, TEntityComponent _component)
          {
              if (!Components.ContainsKey(_type))
                  return false;
      
              Components.Remove(_type);
              return base.Remove(_component);
          }
      
          public IEntityComponent Get(Type _type)
          {
              if (Contains(_type))
                  return Components[_type];
              return null;
          }
          public T Get<T>() where T : IEntityComponent
          {
              return (T)Get(typeof(T));
          }
      
          public bool TryGetValue(Type _type, out TEntityComponent _component)
          {
              return Components.TryGetValue(_type, out _component);
          }
          public bool TryGetValue<T>(out TEntityComponent _component) where T : IEntityComponent
          {
              return TryGetValue(typeof(T), out _component);
          }
      
          public bool Contains(Type _type)
          {
              return Components.ContainsKey(_type);
          }
          public new bool Contains(TEntityComponent _component)
          {
              return Contains(_component.GetType());
          }
          public bool Contains<T>() where T : IEntityComponent
          {
              return Contains(typeof(T));
          }
      
          #endregion
      }
      

      然后将Blueprint改为:

      public sealed class Blueprint
      {
          public ComponentTable<IEntityComponent> Components { get; set; }
      

      并且列表内容将被反序列化。不过……

    3. 您的ComponentTable 继承自List&lt;T&gt;,需要覆盖Add()。但是Add() 不是虚拟的,因此您使用的是public new Add()。这里的问题是,如果有人将您的类转换为List&lt;T&gt; 并调用Add(),您的方法将不会被调用。特别是fastJSON在反序列化的时候是不会调用的!所以你的类型字典在反序列化过程中永远不会被初始化,违背了目的。

      看起来你正在做的是重新发明KeyedByTypeCollection&lt;TItem&gt;。而不是这样做,你可以使用它。使用这个类,你的ComponentTable 变得非常简单:

      public class ComponentTable<TEntityComponent> : KeyedByTypeCollection<TEntityComponent> where TEntityComponent : IEntityComponent
      {
          public void Add(Type _type)
          {
              if (Contains(_type))
                  return;
              Add((TEntityComponent)Activator.CreateInstance(_type));
          }
      
          public void Add<T>() where T : IEntityComponent, new()
          {
              Add(typeof(T));
          }
      }
      

      现在可以在 Microsoft .Net 上对集合进行序列化和反序列化,而不会损坏数据。不过……

    4. 我不确定KeyedByTypeCollection&lt;TItem&gt; 是否统一存在。如果没有,您可能需要移植它。请参阅参考源KeyedByTypeCollection.cs 和基类源keyedcollection.cs。可以在这里找到一种尝试:Alternative to KeyedByTypeCollection in Mono .Net

    5. 作为替代方案,您可以考虑使用 Json.NET。 Json.NET 支持通过TypeNameHandling setting 序列化多态类型。但是,它可以在 unity 上使用吗?

      Google suggests 有一些统一的端口。这里还有各种问题tagged with both Json.NET and unity3d,比如JSON .Net Unity Deserialize dictionary of anonymous objects。因此,您可以进一步调查该选项。

    【讨论】:

    • 严重的最佳答案 10/10。你比我预期的要详细得多,非常感谢。我没有意识到 KeyedByTypeCollection 类甚至存在,我将尝试改变它并查看它如何与 fastJSON 一起使用。我假设您尝试使用 fastJSON 对其进行序列化并且它有效?老实说,我可以使用不同的 JSON,即使 fastJSON 不是统一的一部分,我必须导入它,它恰好是我发现的第一个。此外,它看起来很快,并且有很多功能。但它一直遇到这样的问题,所以我可能会改变它
    • @Lolop - 我在 Microsoft .Net 上成功使用 fastJSON 成功往返 KeyedByTypeCollection&lt;TItem&gt;。我没有团结,所以我无法在那里测试。
    • 你是上帝派来的。我确实必须将这些类复制到统一中,并修复任何不适用于统一的东西。唯一的问题是他们用什么来抛出错误,我不得不用正常的异常替换它们(希望不会对我不利)。这是因为(我假设)统一使用 .Net 3.5,而他们在 KeyedByType 及其基类中使用的东西是 .Net 4.5+? (也可能是为什么它一开始就不统一)。再次非常感谢你,我可能永远也想不通。私有设置正在停止反序列化,但通用设置正在破坏它。
    猜你喜欢
    • 1970-01-01
    • 2012-10-05
    • 2016-10-25
    • 1970-01-01
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    相关资源
    最近更新 更多