将具有基础对象数组的 class 反序列化为具有继承对象数组 (Newtonsoft.Json) 的 class
Deserialize class with an array of base objects into class with an array of inherited objects (Newtonsoft.Json)
我一直在开发一款音乐游戏,并决定添加其他游戏关卡的转换。我决定从使用 JSON 来存储它的关卡的游戏之一,所以我使用 Newtonsoft.Json 来反序列化关卡数据。级别可以有 2 种对象类型,它们存储在一个 array/list 中,其中一个共享 属性 和一个单独的 属性。牢记这一点,我用它的属性制作了级别 class,一个基础和两个继承的 classes:
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
仍然只有基础 classes在里面。
基本上我需要从文件加载级别,将其反序列化为 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
我做错了什么?
我尝试使用此代码:
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
有NoteSingle
和NoteRain
两种音符。也许您没有在 Serialize
中使用 TypeNameHandling.All
。您需要在 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
class 的类型,请使用临时变量来存储和填充稍后,当你读到一个你知道来自具体 class 的 属性 时,创建 Note
实例并用这个变量填充。之后,如果您阅读了此 class 的更多数据,请继续填充您的实例。
用法:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new YourJsonConverter());
var chart = JsonConvert.DeserializeObject<Chart>(json, settings);
我一直在开发一款音乐游戏,并决定添加其他游戏关卡的转换。我决定从使用 JSON 来存储它的关卡的游戏之一,所以我使用 Newtonsoft.Json 来反序列化关卡数据。级别可以有 2 种对象类型,它们存储在一个 array/list 中,其中一个共享 属性 和一个单独的 属性。牢记这一点,我用它的属性制作了级别 class,一个基础和两个继承的 classes:
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
仍然只有基础 classes在里面。
基本上我需要从文件加载级别,将其反序列化为 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
我做错了什么?
我尝试使用此代码:
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
有NoteSingle
和NoteRain
两种音符。也许您没有在 Serialize
中使用 TypeNameHandling.All
。您需要在 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
class 的类型,请使用临时变量来存储和填充稍后,当你读到一个你知道来自具体 class 的 属性 时,创建 Note
实例并用这个变量填充。之后,如果您阅读了此 class 的更多数据,请继续填充您的实例。
用法:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new YourJsonConverter());
var chart = JsonConvert.DeserializeObject<Chart>(json, settings);