Десериализовать класс с массивом базовых объектов в класс с массивом унаследованных объектов (Newtonsoft.Json)

Я работал над музыкальной игрой и решил добавить конвертацию уровней других игр. Одна из игр, которую я решил преобразовать, использует JSON для хранения своих уровней, поэтому я использую Newtonsoft.Json для десериализации данных уровней. Уровень может иметь 2 типа объектов, которые хранятся в одном массиве/списке с одним общим свойством и одним индивидуальным свойством. Имея это в виду, я создал класс уровня с его свойствами, одним базовым и двумя унаследованными классами:

class Chart
{
   //Some unimportant properties
   //...

   public Note[] note;

   class Note
   {
      public int[] beat;
   }

   class NoteSingle : Note
   {
      public int x;
   }

   class NoteRain : Note
   {
      public int[] endbeat;
   }
}

Однако, когда я пытаюсь десериализовать уровень, note содержит только базовые объекты. Я попытался создать JsonSerializerSettings с TypeNameHandling, установленным на All, и передать его методу десериализации, но это не сработало, note по-прежнему содержит только базовые классы.

По сути, мне нужно загрузить уровень из файла, десериализовать его как Chart и сделать каждую из заметок в note одним из типов, унаследованных от Note, в зависимости от данных json. Например, если в заметке есть поле x, загрузите его как NoteSingle, а если в заметке есть поле endbeat, загрузите его как NoteRain.

Представляет

class Note
{
    public int[] beat;
}

class NoteSingle : Note
{
    public int x;
}

class NoteRain : Note
{
    public int[] endbeat;
}

class Chart
{
    //Some unimportant properties
    public Note[] note;
    //Some unimportant properties
}

public static void Convert(string path)
{
    string rawData = File.ReadAllText(path);
    JsonSerializerSettings setts = new JsonSerializerSettings()
    {
        TypeNameHandling = TypeNameHandling.All
    };
    Chart ch = JsonConvert.DeserializeObject<Chart>(rawData, setts);

    //Level converter code
}

Пример данных, которые я пытаюсь загрузить: https://pastebin.com/zgnRsgWZ

Что я делаю не так?

Пожалуйста, дайте минимальный воспроизводимый пример и другие объяснения того, чего вы хотите достичь. Да, если вы десериализуете тип как базовый тип, он будет знать только свойства базового типа. То же самое для любого наследования: если вы получаете доступ к note из C#, вы также не можете получить доступ к x и тому подобное.

JHBonarius 17.05.2022 10:21

В основном мне нужно загрузить все заметки из файла в массив и сделать каждую из них одним из унаследованных типов в зависимости от данных json. Например, если в заметке есть поле x, загрузите его как NoteSingle, а если в заметке есть поле endbeat, загрузите его как NoteRain. Что касается представителей, дайте мне немного времени, я отредактирую этот комментарий.

HiLord Reilo 17.05.2022 12:54

Вам действительно нужно указать все соответствующие данные в вопросе. Не в комментариях, как вы сейчас. Переполнение стека — это не форум. Ваша ситуация очень специфична. Общего решения нет.

JHBonarius 17.05.2022 13:21

Хорошо, я отредактирую сообщение через минуту

HiLord Reilo 17.05.2022 13:26
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
1
4
39
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я пытаюсь с этим кодом:

var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};

var chart = new Chart();
chart.note = new Note[]
{
        new NoteSingle { x = 37 },
        new NoteRain { endbeat = new[] { 9 } }
};

var json = JsonConvert.SerializeObject(chart, settings);
var chart2 = JsonConvert.DeserializeObject<Chart>(json, settings);

И это работает. json имеет это значение:

{
   "$type":"Test.Chart, SoApp",
   "note":{
      "$type":"Test.Chart+Note[], SoApp",
      "$values":[
         {
            "$type":"Test.Chart+NoteSingle, SoApp",
            "x":37,
            "beat":null
         },
         {
            "$type":"Test.Chart+NoteRain, SoApp",
            "endbeat":{
               "$type":"System.Int32[], mscorlib",
               "$values":[
                  9
               ]
            },
            "beat":null
         }
      ]
   }
}

И chart2 имеет 2 ноты типа NoteSingle и NoteRain. Возможно, вы не используете TypeNameHandling.All в Serialize. Вы должны использовать оба на Serialize и Deserialize.

ОБНОВИТЬ

Если вы не контролируете сгенерированный JSON, вы можете использовать конвертер для его десериализации:

public class YourJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var chart = new Chart();
        var notes = new List<Note>();

        string name = null;
        NoteRain noteRain = null;

        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.PropertyName:
                    name = reader.Value.ToString();
                    break;
                    
                case JsonToken.Integer:
                    if (name == "x")
                    {
                        var note = new NoteSingle { x = Convert.ToInt32(reader.Value) };
                        notes.Add(note);
                    }
                    else if (name == "endbeat")
                    {
                        if (noteRain == null)
                        {
                            noteRain = new NoteRain { endbeat = new[] { Convert.ToInt32(reader.Value) } };
                            notes.Add(noteRain);
                        }
                        else
                        {
                            var array = noteRain.endbeat;
                            noteRain.endbeat = new int[noteRain.endbeat.Length + 1];
                                
                            for (int i = 0; i < array.Length; i++)
                            {
                                noteRain.endbeat[i] = array[i];
                            }

                            noteRain.endbeat[noteRain.endbeat.Length - 1] = Convert.ToInt32(reader.Value);
                        }
                    }

                    break;
            }
        }

        chart.note = notes.ToArray();
        return chart;
    }

    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType)
    {
        return true;
    }
}

Это простой пример, вы должны настроить его, но я думаю, что это легко сделать. В имени свойства я получаю имя свойства и использую его позже для создания правильного типа. Если я обрабатываю свойство x, я знаю, что это NoteSingle, и я создаю его и добавляю в список notes. Если, например, вы получили имя свойства, такое как beat, и вы еще не знаете тип класса Note, используйте временную переменную для хранения и заполнения, а затем, когда вы читаете свойство, которое, как вы знаете, принадлежит конкретному классу. , создайте экземпляр Note и заполните этой переменной. И после этого, если вы читаете больше данных этого класса, продолжайте заполнять свой экземпляр.

Использование:

var settings = new JsonSerializerSettings();
settings.Converters.Add(new YourJsonConverter());
var chart = JsonConvert.DeserializeObject<Chart>(json, settings);

Нет, я не пытаюсь его сериализовать, мне нужно только десериализовать данные. Вот пример уровня, который мне нужно загрузить: pastebin.com/zgnRsgWZ

HiLord Reilo 17.05.2022 12:44

Другие вопросы по теме