Я работал над музыкальной игрой и решил добавить конвертацию уровней других игр. Одна из игр, которую я решил преобразовать, использует 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
Что я делаю не так?
В основном мне нужно загрузить все заметки из файла в массив и сделать каждую из них одним из унаследованных типов в зависимости от данных json. Например, если в заметке есть поле x
, загрузите его как NoteSingle
, а если в заметке есть поле endbeat
, загрузите его как NoteRain
. Что касается представителей, дайте мне немного времени, я отредактирую этот комментарий.
Вам действительно нужно указать все соответствующие данные в вопросе. Не в комментариях, как вы сейчас. Переполнение стека — это не форум. Ваша ситуация очень специфична. Общего решения нет.
Хорошо, я отредактирую сообщение через минуту
Я пытаюсь с этим кодом:
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
Пожалуйста, дайте минимальный воспроизводимый пример и другие объяснения того, чего вы хотите достичь. Да, если вы десериализуете тип как базовый тип, он будет знать только свойства базового типа. То же самое для любого наследования: если вы получаете доступ к
note
из C#, вы также не можете получить доступ кx
и тому подобное.