我正在尝试将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;
}
}
可以使反序列化工作吗?
谢谢!
答案 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;
}
}