Code: Select all
internal interface ITable
{
int Number { get; }
ISection Section { get; }
}
internal class Table : ITable
{
public int Number { get; private init; }
public ISection Section { get; internal set; }
private Table(int number)
{
Number = number;
}
internal static Table CreateTable(int number)
{
return new Table(number);
}
}
internal interface ISection
{
int Number { get; }
IReadOnlyList Tables { get; }
}
internal class Section : ISection
{
public int Number { get; private init; }
public IReadOnlyList Tables { get; internal set; }
private Section(int number)
{
Number = number;
}
internal static Section CreateSection(int number)
{
return new Section(number);
}
}
Und ich erstelle (mit einer Referenzschleife) und serialisiere sie auf folgende Weise:
Code: Select all
var table = Table.CreateTable(1);
var section = Section.CreateSection(1);
table.Section = section;
section.Tables = new List { table };
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
TypeNameHandling = TypeNameHandling.All,
ContractResolver = new CustomContractResolver()
};
var json = JsonConvert.SerializeObject(section, settings);
var restored = JsonConvert.DeserializeObject(json, settings);
Code: Select all
internal class CustomContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
prop.Readable = true;
if (!prop.Writable)
{
switch (member.MemberType)
{
case MemberTypes.Field:
prop.Writable = true;
break;
case MemberTypes.Property:
var hasPrivateSetter = (member as PropertyInfo)!.GetSetMethod(true) != null;
prop.Writable = hasPrivateSetter;
break;
}
}
return prop;
}
protected override List GetSerializableMembers(Type objectType)
{
var members = base.GetSerializableMembers(objectType);
var nonPublicFields = objectType.GetFields(
BindingFlags.Instance |
BindingFlags.NonPublic)
.Where(f => !f.Name.EndsWith("k__BackingField")).ToList(); // excluding backing fields from auto-properties
members.AddRange(nonPublicFields);
var nonPublicProperties = objectType.GetProperties(
BindingFlags.Instance |
BindingFlags.NonPublic).ToList();
members.AddRange(nonPublicProperties);
return members;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// if Newtonsoft.Json was not able to find suitable constructor - helping him to find a non public constructor
if (contract.OverrideCreator == null && contract.DefaultCreator == null && !objectType.IsInterface && !objectType.IsAbstract)
{
var constructor = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.FirstOrDefault(c => c.GetParameters().Length > 0);
if (constructor != null)
{
var creatorParameters = new List();
foreach (var param in constructor.GetParameters())
{
var prop = contract.Properties.GetClosestMatchProperty(param.Name);
if (prop != null)
{
creatorParameters.Add(new JsonProperty
{
PropertyName = prop.PropertyName,
PropertyType = param.ParameterType,
DeclaringType = constructor.DeclaringType,
ValueProvider = prop.ValueProvider
});
}
else
{
creatorParameters = null;
break;
}
}
if (creatorParameters != null)
{
contract.OverrideCreator = constructor.Invoke;
contract.CreatorParameters.Clear();
foreach (var param in creatorParameters)
{
contract.CreatorParameters.Add(param);
}
}
}
}
return contract;
}
}

Wenn ich einen öffentlichen Standardkonstruktor hinzufüge Tabelle und Abschnitt – es funktioniert wie erwartet (es ist nur ein Experiment – ich kann keine Datenklassen ändern und Konstruktoren oder Newtonsoft-Attribute hinzufügen). Es sieht also so aus, als ob etwas mit meiner CreateObjectContract-Methode nicht stimmt.
Was ist mein Fehler?
Mobile version