Десерализация JSON в абстрактный список с использованием DataContractJsonSerializer - PullRequest
9 голосов
/ 19 октября 2010

Я пытаюсь десериализовать файл JSon в экземпляр класса, который содержит абстрактный список.Сериализация экземпляра для Json работает хорошо (проверьте файл json ниже).При десериализации я получаю «System.MemberAccessException» с сообщением «Не удается создать абстрактный класс».Обсисуули десерализатор пытается создать экземпляр абстрактного класса, а не конкретного класса.

В моем примере десериализованный класс называется 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 Ответ

10 голосов
/ 20 октября 2010

Мы нашли, почему это не работает. Сразу после сериализации объекта мы идентифицируем полученную строку для большей читаемости. Затем мы записываем строку в файл:

    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;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...