c# – JSON deseralization to abstract list using DataContractJsonSerializer – Education Career Blog

I’m trying to deserialize a JSon file to an instance of a class that contains an abstract list. Serializing the instance to the Json works well (check the json file below). When deserializing I get a “System.MemberAccessException” with the message “Cannot create an abstract class”. Obvisouly the deseralizer is trying to instantiate the abstract class and not the 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 the Json file that was serialized and that I’m trying to deserialize. Notice the “__type” field for the deserializer to create the concrete classes :

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

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

It is possible to make the deserialization work ?

Thanks !

,

We’ve found why it wasn’t working. Just after the serialization of the object we ident the resulting string for more readability. Then we write the string into a 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());
        }
    }

The identation is actually the reason why the deserialization was not working. It seems the parser of the DataContractJsonSerializer is really picky. If some characters are between the character { and the field “__type”, the serializer get lost.

For example this string will serialize correctly :

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

But this next string will not serialize.

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

The only difference is the space characters before the “__type”. The serialization will throw a MemberAccessException. This is misleading because this behavior appears only when deserializing into an abstract List. Serializing into an abstract field works fine no matter the characters.

To fix this issue without removing the readability of the file, The string can be modified before the deseralization. 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;
        }
    }

Leave a Comment