JSON desararalization to abstract list using DataContractJsonSerializer

I am trying to deserialize a JSon file for an instance of a class containing an abstract list. Serializing a Json instance works well (check out the json file below). When deserializing, I get a "System.MemberAccessException" with the message "Unable to create an abstract class." Obvisouly deseralizer is trying to instantiate an abstract class, not a concrete class.

In my example, the deserialized class is called 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; } } } 

This is a Json file that has been serialized and that I am trying to deserialize. Pay attention to the __type field for the deserializer to create specific classes:

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

Below is the code I'm using for deserialization:

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

Is it possible to perform deserialization?

Thank!

+8
json list c # serialization abstract
Oct 19 2018-10-18
source share
1 answer

We found out why it does not work. Immediately after serializing the object, we identify the resulting string for greater readability. Then we write the line to the file:

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

Identification is actually the reason that deserialization did not work. The DataContractJsonSerializer parser seems to be really picky. If some characters are between the {character and the "__type" field, the serializer is lost.

For example, this line will be serialized correctly:

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

But this next line will not be serialized.

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

The only difference is the space characters before "__type". Serialization will raise a MemberAccessException. This is misleading because this behavior only appears when deserializing into an abstract list. Serialization in an abstract field works fine regardless of characters.

To fix this problem without deleting the readability of the file, this line can be changed before deletion. For example:

  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; } } 
+10
Oct 19 '10 at 22:59
source share



All Articles