Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.87/15: Рейтинг темы: голосов - 15, средняя оценка - 4.87
0 / 0 / 1
Регистрация: 18.01.2017
Сообщений: 2
1
.NET 4.x

Десериализация классов наследников

18.01.2017, 11:34. Показов 2784. Ответов 2
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Для теста имеется такой набор классов:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[KnownType(typeof(C))]
    [KnownType(typeof(B))]
    [KnownType(typeof(List<A>))]
    public abstract class A
    {
        public int id { get; set; }
    }
 
    public class B : A
    {
        public string name { get; set; }
    }
 
    public class C : A
    {
        public string address { get; set; }
        public List<A> nodes { get; set; } = new List<A>();
    }


Создаю экземпляры:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
var C = new C()
            {
                id = 0,
                address = "sov1"
            };
 
            C.nodes.Add(new B() { id = 1, name = "name1" });
            C.nodes.Add(new C() { id = 2, address = "adr1" });


Пытаюсь серелизовать и десерелизовать:
C#
1
2
3
string json = JsonConvert.SerializeObject(C);
 
var testBack = JsonConvert.DeserializeObject<C>(json, new KnownTypeConverter());
Десерилизуется неправильно, а именно в nodes все элементы становятся класса C. Хотя должен быть B и C.

Вот конвертер:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // Load JObject from stream
            JObject jObject = JObject.Load(reader);
 
            // Create target object based on JObject
            Attribute[] attrs = Attribute.GetCustomAttributes(objectType);  // Reflection. 
 
            // check known types for a match. 
            foreach (var attr in attrs.OfType<KnownTypeAttribute>())
            {
                object target = Activator.CreateInstance(attr.Type);
 
                JObject jTest;
                using (var writer = new StringWriter())
                {
                    using (var jsonWriter = new JsonTextWriter(writer))
                    {
                        serializer.Serialize(jsonWriter, target);
                        string json = writer.ToString();
                        jTest = JObject.Parse(json);
                        //jTest = JArray.Parse(json);
                    }
                }
 
                var jO = this.GetKeys(jObject).Select(k => k.Key).ToList();
                var jT = this.GetKeys(jTest).Select(k => k.Key).ToList();
 
                //if (jO.Count == jT.Count && jO.Intersect(jT).Count() == jO.Count)
                {
                    serializer.Populate(jObject.CreateReader(), target);
                    return target;
                }
            }
 
            return null;
            //throw new SerializationException(string.Format("Could not convert base class {0}", objectType));
        }


Как сделать, чтобы десерелизовалось правильно? Спасибо.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
18.01.2017, 11:34
Ответы с готовыми решениями:

Десериализация разных классов с одним названием
Проблема состоит вот в чем. Изначально был выбран не слишком удачный способ сериализации, а...

Сериализация и десериализация в Json нескольких разных классов
Добрый день. Стоит такая задача. Есть приложение которое сериализует данные, есть второе...

Вызов методов у классов наследников
Собственно есть код: class A { public virtual void M(int param) { ...

Полчение массива наследников из листа базовых классов
Привет всем. Такой вопросец назрел, чет не могу никак к его решению подступиться. Есть базовый...

2
Эксперт .NET
5531 / 4296 / 1216
Регистрация: 12.10.2013
Сообщений: 12,331
Записей в блоге: 2
18.01.2017, 12:39 2
Цитата Сообщение от EugV Посмотреть сообщение
Как сделать, чтобы десерелизовалось правильно
Попробуйте вот так.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
 
namespace ConsoleApplication11 {
    class Program {
        static void Main(string[] args) {
            var c = new c { id = 0, address = "test_address" };
            c.nodes.Add(new b { id = 1, name = "name_of_b" });
            c.nodes.Add(new c { id = 2, address = "address_of_c" });
 
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(a));
            using (Stream fs = File.OpenWrite("test.json")) {
                serializer.WriteObject(fs, c);
            }
 
            c cObj = null;
            using (Stream fs = File.OpenRead("test.json")) {
                cObj = (c)serializer.ReadObject(fs);
            }
            if (cObj != null) {
                foreach (var o in cObj.nodes) {
                    Console.WriteLine(o.ToString());
                }
            }
            Console.ReadLine();
        }
    }
    [DataContract]
    [KnownType("GetTypes")]
    abstract class a {
        [DataMember]
        public int id { get; set; }
        static Type[] GetTypes() {
            return new[] { typeof(b), typeof(c) };
        }
    }
    [DataContract]
    class b : a {
        [DataMember]
        public string name { get; set; }
 
        public override string ToString() {
            return string.Format("id={0}, name={1}", id, name);
        }
    }
    [DataContract]
    class c : a {
        [DataMember]
        public string address { get; set; }
        [DataMember]
        public List<a> nodes = new List<a>();
 
        public override string ToString() {
            return string.Format("id={0}, address={1}", id, address);
        }
    }
}
1
0 / 0 / 1
Регистрация: 18.01.2017
Сообщений: 2
19.01.2017, 12:05  [ТС] 3
Цитата Сообщение от insite2012 Посмотреть сообщение
Попробуйте вот так.
Да так работает. Я так понял за счет того что в json добавляется информация о типе.

Возможно это сделать без информации о типе в json и с помощью JSON.NET ? Я думаю через кастомные конвертеры, которые JSON.NET позволяет делать, это как-то все-таки возможно.

Добавлено через 46 минут
Нашел способ как сделать тоже самое с json.net. Достаточно настройку сделать:
C#
1
2
3
4
var jsonSerializerSettings = new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All,
            };
Тогда тоже информация о типе в json будет сохраняться.
0
19.01.2017, 12:05
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
19.01.2017, 12:05
Помогаю со студенческими работами здесь

Как заполнить List<MyClass> случайным количеством классов наследников?
Добрый день. Что-то никак не могу сделать следующее: Есть абстрактный класс Room и классы...

Сериализация наследников известных классов
Есть WCF-сервис, возвращающий объекты класса MyEntity, соответственно класс MyEntity включен в...

Реализация классов-наследников или классов-родителей
Не могу переделать функционал программы таким образом, чтобы у появились классы-наследники, или...

Наследование. Неправильная работа одного из классов наследников
Привет, помогите пожалуйста. Есть базовый класс и 2 его наследника (audio и book), у которых по 2...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru