问题
I would like to know how to read the JSON with a dynamic layout into my object that has an interface IPeople
. When I try I get an error:
'Could not create an instance of type 'xxxx'. Type is an interface or abstract class and cannot be instantiated.
I have spent 2 days searching through Google and I tried using converters and it only works for one object at a time So I would have to choose between a Staff converter and a spectator converter.
How can I get both objects to load using the interface IPeople
that loads to the Staff
object if the staff data is detected and Spectator
object if the spectator data is detected (possibly more object types all have unique properties).
{
"appCore":
{
"Crowd":
[
{
"Name": "Name1",
"Staff":
{
"PopupText":
[
{ "DisplayName":"No Popup Text","PopupValue":"", "IsSeperator":false},
{ "DisplayName":"--------------","PopupValue":"", "IsSeperator":true},
{ "DisplayName":"HT","PopupValue":"HT", "IsSeperator":false}
]
},
"Spectator":
{
"PopupText2":
[
{ "DisplayName":"No Popup Text","PopupValue":"", "Cheese":"hiih"},
{ "DisplayName":"--------------","PopupValue":"", "Cheese":"hiih"},
{ "DisplayName":"Half-Time","PopupValue":"Half-Time", "Cheese":"hiih"}
]
}
}
]
}
}
C# Models:
public class Crowd : ICrowd
{
public IPeople People { get; set; }
}
public class Staff : IPeople
{
IList<PopupTextData> PopupText { get; set; }
}
public class Spectator : IPeople
{
IList<PopupTextData2> PopupText2 { get; set; }
}
public class PopupTextData
{
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public bool IsSeperator { get; set; }
}
public class PopupTextData2
{
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public string Cheese { get; set; }
}
Code used to read the data:
settings.ForEach(type =>
{
builder.Register(c => c.Resolve<MCoreReader>().LoadSection(type))
.As(type.GetInterfaces())
.AsImplementedInterfaces()
.InstancePerRequest()
.InstancePerLifetimeScope();
});
public static object LoadSection(Type type, string _configurationFilePath, string _sectionNameSuffix)
{
if (!File.Exists(_configurationFilePath))
return Activator.CreateInstance(type);
var jsonFile = File.ReadAllText(_configurationFilePath);
var section = ToCamelCase(type.Name.Replace(_sectionNameSuffix, string.Empty));
var settingsData = JsonConvert.DeserializeObject<dynamic>(jsonFile, JsonSerializerSettings);
var settingsSection = settingsData[section];
return settingsSection == null
? Activator.CreateInstance(type)
: JsonConvert.DeserializeObject(JsonConvert.SerializeObject(settingsSection), type, JsonSerializerSettings);
}
private class SettingsReaderContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p =>
{
p.Writable = true;
p.Readable = true;
});
return props;
}
}
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
ContractResolver = new SettingsReaderContractResolver(),
};
回答1:
Assuming you control the Json, here's a working example: I've changed the class structure a bit.
It uses TypeNameHandling.All which writes the type information into the Json - as $type
-, so when it's deserialized (using the same setting), it can restore the type.
void Main() {
var crowd = new Crowd {
People = new List<IPeople> {
new Staff {
PopupText = new List<PopupTextData> {
new PopupTextData { DisplayName = "Staff"}
}},
new Spectator {
PopupText = new List<PopupTextData> {
new PopupTextData { DisplayName = "Spectator"}
}},
}
};
var settings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
};
var json = JsonConvert.SerializeObject(crowd, settings);
var newCrowd = JsonConvert.DeserializeObject<ICrowd>(json, settings);
Console.WriteLine(newCrowd.People.Count); // outputs '2'
}
public interface ICrowd {
IList<IPeople> People { get; set; }
}
public interface IPeople {
IList<PopupTextData> PopupText { get; set; }
}
public class Crowd : ICrowd {
public IList<IPeople> People { get; set; }
}
public class Staff : IPeople {
public IList<PopupTextData> PopupText { get; set; }
}
public class Spectator : IPeople {
public IList<PopupTextData> PopupText { get; set; }
}
public class PopupTextData {
public string DisplayName { get; set; }
public string PopupValue { get; set; }
public bool IsSeperator { get; set; }
}
来源:https://stackoverflow.com/questions/62462147/how-to-read-with-interfaces-in-the-model