Deserialisierung über Newtonsoft für Klassen mit einer Referenzschleife, privaten Konstruktoren und Settern privater/intC#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Deserialisierung über Newtonsoft für Klassen mit einer Referenzschleife, privaten Konstruktoren und Settern privater/int

Post by Anonymous »

Ich habe ein paar Klassen (vereinfacht):

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);
}
}
Wie Sie sehen können, haben Klassen private Konstruktoren und private/interne Eigenschaftssetzer.
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);
In meinem CustomContractResolver sammle ich alle nicht öffentlichen Mitglieder und auch wenn Newtonsoft keinen geeigneten Konstruktor finden konnte, helfe ich ihm, einen nicht öffentlichen Konstruktor zu finden:

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;
}
}
Aber nach der Deserialisierung, Section.Tables[0].Section=null (sieht so aus, als hätte die Option PreserveReferencesHandling nicht wie erwartet funktioniert):
Image

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?

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post