在JSon Serializer C#中转换服装类中的动态类型时出错

时间:2015-03-07 07:01:06

标签: c# json serialization json.net dynamictype

我有一个由许多类实现的接口,在我的接口中,我有一个由子类填充的类,它实现了我的接口。 我的工作是,我发现实现我的接口的类并调用填充的类,然后我通过NewSoft.Json序列化这些类并写入文件然后在另一个地方我反序列化该文件并创建我的根类的实例并传递它到想要阅读和使用它的类。 我的问题是当我反序列化并填写我的属性时,我无法使用我的数据属性,因为json不能给我正确的东西。

在我的测试场景中的示例我有一个类填充我的类属性并在Data Proprties中归档DataTable然后我序列化它并且当我反序列化ir并且想要将它再次转换为datatable时有一些错误 我得到数据属性的类型,并说是Newtonsoft.Json.Linq.JArray 我的另一个问题是如何编写可以转换所有类填充它的类型的公共代码 我的代码是Blow:

我的界面:

 public interface ISyncable
        {
            List<SyncablePackage> GetSyncableEntityDetails();
            void SetSyncablePackage(SyncablePackage syncablepackage);
            List<Guid> DependentSoftwares { get; set; }
        }

我的班级:

[Serializable]
[DataContract]
public class SyncablePackage : ISerializable,IDisposable
{
    public SyncablePackage() { }
    public SyncablePackage(SerializationInfo info, StreamingContext ctxt)
    {
        //Get the values from info and assign them to the appropriate properties
        EntityGuid = (Guid)info.GetValue("EntityGuid", typeof(Guid));
        Name = (string)info.GetValue("Name", typeof(string));
        Data = (object)info.GetValue("Data", typeof(object));
        DataFileID = (List<int>)info.GetValue("DataFileID", typeof(List<int>));
    }
    //Serialization function.
    public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
    {
        info.AddValue("EntityGuid", EntityGuid);
        info.AddValue("Name", Name);
        info.AddValue("Data", Data);
        info.AddValue("DataFileID", DataFileID);
    }

    [DataMember]
    public Guid EntityGuid;
    [DataMember]
    public string Name;
    [DataMember]
    public object Data;
    [DataMember]
    public List<int> DataFileID;

    [DataMember]
    private IntPtr handle = new IntPtr();
    [DataMember]
    private Component component = new Component();
    [DataMember]
    private bool disposed = false;
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                Name = null;
                Data = null;
                DataFileID = null;
                component.Dispose();
            }
            CloseHandle(handle);
            handle = IntPtr.Zero;
            disposed = true;
        }
    }

    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);
    ~SyncablePackage()
    {
        Dispose(false);
    }

}

示例类填充数据 然后我得到它并像打击一样序列化:

#region Handle Null Value
                    if (package.DataFileID == null)
                        package.DataFileID = new List<int>() { int.MinValue };
                    if (package.Data == null)
                        package.Data = new DataTable();
                    if (package.Name == null)
                        package.Name = "NoName";
                    if (package.EntityGuid == null)
                        package.EntityGuid = Guid.Empty;
                    #endregion

                    using (StreamWriter stream = new StreamWriter(fileName))
                    {
                        try
                        {
                            stream.BaseStream.Seek(0, SeekOrigin.Begin);
                            stream.Write(JsonConvert.SerializeObject(package));
                        }
                        finally
                        {
                            stream.BaseStream.Flush();
                            stream.Close();
                            stream.Dispose();
                        }
                    }

在其他地方我得到文件并使用如下:

using (StreamReader stream = new StreamReader(fileName))
                    {
                        try
                        {
                            stream.BaseStream.Seek(0, SeekOrigin.Begin);
                            var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DateFormatHandling = DateFormatHandling.MicrosoftDateFormat };
                            var sss = JsonConvert.DeserializeObject<SyncablePackage>(stream.ReadToEnd(), settings);
                        }
                        finally
                        {
                            stream.BaseStream.Flush();
                            stream.Close();
                            stream.Dispose();
                        }
                    }

我在文件中获取数据:

{"EntityGuid":"4a6e20fd-e0c1-451b-870d-645162cbae60","Name":"AccessZones","Data":[{"ID":13,"Code":"23","Title":"(3)","Active":false,"Deleted":true,"Guid":"bdbc92c4-4246-4695-b36c-49b4911d7abe","ModificationDate":"2015-01-05T17:25:59.763","ModificationServerGuid":"33333333-3333-3333-3333-333333333333"},{"ID":15,"Code":"21","Title":" (2)","Active":false,"Deleted":true,"Guid":"b304aed4-d175-4d69-91ae-b98176e471f4","ModificationDate":"2015-01-05T17:25:59.763","ModificationServerGuid":"33333333-3333-3333-3333-333333333333"},{"ID":16,"Code":"22","Title":" (3)","Active":false,"Deleted":true,"Guid":"cdc03816-83e8-41dc-bd43-2681a3e3438c","ModificationDate":"2015-01-05T17:25:59.763","ModificationServerGuid":"33333333-3333-3333-3333-333333333333"}],"DataFileID":[-2147483648]}

任何人都可以告诉我忘了什么吗?

1 个答案:

答案 0 :(得分:0)

您的问题出现是因为您已将Data声明为object而非其实际类型:

public object Data;

当Json.NET尝试反序列化这样的字段时,它没有关于所需类型的信息,因此将其反序列化为JToken(当然是object的子类)来保存从文件中读取的数据。

最简单的解决方法是将Data声明为其实际类型:

public DataTable Data { get; set; }

如果你不能这样做(因为你实际上使用的是typed DataSet类型可能在运行时变化的类型表),那么普通解决方案将是设置TypeNameHandling = TypeNameHandling.All。但是,这对DataTable不起作用,因为DataTable通过转换器[{3}} - 和转换器DataTableConverter存储。

解决方法是创建一个不需要转换器且可以保存类型信息的中间通用代理对象:

public abstract class JsonTypeWrapper
{
    protected JsonTypeWrapper() { }

    [IgnoreDataMember]
    public abstract object ObjectValue { get; }

    public static object CreateWrapper<T>(T value)
    {
        if (value == null)
            return new JsonTypeWrapper<T>();
        var type = value.GetType();
        if (type == typeof(T))
            return new JsonTypeWrapper<T>(value);
        // Return actual type of subclass
        return Activator.CreateInstance(typeof(JsonTypeWrapper<>).MakeGenericType(type), value);
    }
}

[DataContract]
public sealed class JsonTypeWrapper<T> : JsonTypeWrapper
{
    public JsonTypeWrapper() : base() { }

    public JsonTypeWrapper(T value) : base()
    {
        this.Value = value;
    }

    public override object ObjectValue
    {
        get { return Value; }
    }

    [DataMember]
    public T Value { get; set; }
}

然后在你的课堂上使用它:

[DataContract]
public partial class SyncablePackage 
{
    [IgnoreDataMember]
    public object Data;

    [DataMember(Name="Data")]
    [JsonProperty("Data", TypeNameHandling = TypeNameHandling.All)] // REQUIRE output of type information.
    object DataWrapper
    {
        get
        {
            if (Data == null)
                return null;
            return JsonTypeWrapper.CreateWrapper(Data);
        }
        set
        {
            var wrapper = value as JsonTypeWrapper;
            if (wrapper == null)
            {
                if (value != null)
                    Debug.WriteLine("Invalid incoming data type: " + value.ToString());
                Data = null;
                return;
            }
            Data = wrapper.ObjectValue;
        }
    }
}

生成的JSON看起来像:

{
  "Data": {
    "$type": "MyNamespace.JsonTypeWrapper`1[[MyNamespace.MyDataSetType+MyDataTableType, MyAssemblyName]], MyAssemblyName",
    "Value": [
      {
        "Value": "2015-03-07T05:00:00Z"
      },
    ]
  }
}

<强>更新

注意到您使用ISerializable ,这会使事情变得更复杂,因为JsonPropertyAttribute无法与ISerializable一起使用。相反,您必须在$type和流构造函数中强制序列化GetObjectData属性:

[DataContract]
[Serializable]
public partial class SyncablePackage : ISerializable
{
    public SyncablePackage() { }

    public SyncablePackage(SerializationInfo info, StreamingContext ctxt)
    {
        var jObj = (JObject)info.GetValue("Data", typeof(JObject));
        if (jObj != null)
            DataWrapper = jObj.ToObject<object>(new JsonSerializer { TypeNameHandling = TypeNameHandling.All });
        Name = (string)info.GetValue("Name", typeof(string)); // Serialize your other properties as needed.
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Data", JObject.FromObject(DataWrapper, new JsonSerializer { TypeNameHandling = TypeNameHandling.All }));
        info.AddValue("Name", Name); // Deserialize your other properties as needed.
    }

    [IgnoreDataMember]
    public object Data;

    [DataMember(Name = "Data")]
    object DataWrapper
    {
        get
        {
            if (Data == null)
                return null;
            return JsonTypeWrapper.CreateWrapper(Data);
        }
        set
        {
            var wrapper = value as JsonTypeWrapper;
            if (wrapper == null)
            {
                if (value != null)
                    Debug.WriteLine("Invalid incoming data type: " + value.ToString());
                Data = null;
                return;
            }
            Data = wrapper.ObjectValue;
        }
    }
}

更新2

如果您还使用相同的代码进行二进制序列化,则在进行二进制格式化时,您需要确保ISerializable.GetObjectDatapublic SyncablePackage(SerializationInfo info, StreamingContext ctxt)以旧的方式工作 - 尽管用于二进制格式化和JSON格式。您可以使用do not work with type name handling向这些方法发出JSON正在序列化的信号:

/// <summary>
/// Synthetic class to set in StreamingContext.Context
/// </summary>
public sealed class JsonStreamingContext
{
    public JsonStreamingContext()
    {
    }
}

[DataContract]
[Serializable]
public partial class SyncablePackage : ISerializable
{
    public SyncablePackage() { }

    public SyncablePackage(SerializationInfo info, StreamingContext ctxt)
    {
        if (ctxt.Context is JsonStreamingContext)
        {
            var jObj = (JObject)info.GetValue("Data", typeof(JObject));
            if (jObj != null)
                DataWrapper = jObj.ToObject<object>(new JsonSerializer { TypeNameHandling = TypeNameHandling.All });
        }
        else
        {
            Data = (object)info.GetValue("Data", typeof(object));
        }
        Name = (string)info.GetValue("Name", typeof(string)); // Serialize your other properties as needed.
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (context.Context is JsonStreamingContext)
        {
            info.AddValue("Data", JObject.FromObject(DataWrapper, new JsonSerializer { TypeNameHandling = TypeNameHandling.All }));
        }
        else
        {
            info.AddValue("Data", Data);
        }

        info.AddValue("Name", Name); // Deserialize your other properties as needed.
    }

    // Rest as in first update.
}

然后在StreamingContext.Context中设置JsonStreamingContext来序列化:

        var settings = new JsonSerializerSettings { Context = new StreamingContext(StreamingContextStates.All, new JsonStreamingContext()) };
        var json = JsonConvert.SerializeObject(package, settings);

        var package = JsonConvert.DeserializeObject<SyncablePackage>(json, settings);