JSON反序列化使用DataContractJsonSerializer抽象列表

时间:2010-10-19 18:04:55

标签: c# json list serialization abstract

我正在尝试将JSon文件反序列化为包含抽象列表的类的实例。将实例序列化到Json运行良好(检查下面的json文件)。反序列化时,我得到一个带有“无法创建抽象类”消息的“System.MemberAccessException”。显然,deseralizer试图实例化抽象类而不是具体类。

在我的示例中,反序列化的类称为ElementContainer:

namespace Data
{
    [DataContract]
    [KnownType(typeof(ElementA))]
    [KnownType(typeof(ElementB))]
    public class ElementContainer
    {
        [DataMember]
        public List<Element> Elements { get; set; }
    }

    [DataContract]
    public abstract class Element
    {
    }

    [DataContract]
    public class ElementA : Element
    {
        [DataMember]
        int Id { get; set; }
    }

    [DataContract]
    public class ElementB : Element
    {
        [DataMember]
        string Name { get; set; }
    }
}

这是序列化的Json文件,我正在尝试反序列化。注意反序列化器的“__type”字段用于创建具体类:

{
    "Elements":
    [
        {
            "__type":"ElementA:#Data",
            "Id":1
        }, 
        {
            "__type":"ElementB:#Data",
            "Name":"MyName"
        }       
    ]
}

以下是我用于反序列化的代码:

    public T LoadFromJSON<T>(string filePath)
    {
        try
        {
            using (FileStream stream = File.OpenRead(filePath))
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
                T contract = (T)serializer.ReadObject(stream);
                return contract;
            }
        }
        catch (System.Exception ex)
        {
            logger.Error("Cannot deserialize json " + filePath, ex);
            throw;
        }
    }

可以使反序列化工作吗?

谢谢!

1 个答案:

答案 0 :(得分:10)

我们已经找到了它无法正常工作的原因。在对象序列化之后,我们识别结果字符串以获得更多可读性。然后我们将字符串写入文件:

    public void SaveContractToJSON<T>(T contract, string filePath)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            serializer.WriteObject(stream, contract);
            string json = Encoding.UTF8.GetString(stream.ToArray());
            File.WriteAllText(filePath, json.IndentJSON());
        }
    }

标识实际上是反序列化不起作用的原因。似乎DataContractJsonSerializer的解析器真的很挑剔。如果某些字符位于字符{和字段“__type”之间,则序列化程序会丢失。

例如,此字符串将正确序列化:

"{\"Elements\":[{\"__type\":\"ElementA:#Data\",\"Id\":1}]}"

但是这个下一个字符串不会序列化。

"{\"Elements\":[   {\"__type\":\"ElementA:#Data\",\"Id\":1}]}"

唯一的区别是“__type”之前的空格字符。序列化将抛出MemberAccessException。这是误导性的,因为只有在反序列化为抽象列表时才会出现此行为。无论字符如何,序列化为抽象字段都可以正常工作。

要解决此问题而不删除文件的可读性,可以在取消分区之前修改字符串。例如:

    public T LoadContractFromJSON<T>(string filePath)
    {
        try
        {
            string text = File.ReadAllText(filePath);
            text  = Regex.Replace(text, "\\{[\\n\\r ]*\"__type", "{\"__type");
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
                T contract = (T)serializer.ReadObject(stream);
                return contract;
            }
        }
        catch (System.Exception ex)
        {
            logger.Error("Cannot deserialize json " + filePath, ex);
            throw;
        }
    }