// property deserialization defaults /// <summary> /// Returns true if the given property should be deserialized. /// /// Override to tweak behavior. /// </summary> protected virtual bool ShouldDeserialize(TypeInfo forType, PropertyInfo property) { if (property.SetMethod == null) { return(false); } var ignoreDataMember = property.GetCustomAttribute <IgnoreDataMemberAttribute>(); if (ignoreDataMember != null) { return(false); } var dataMember = property.GetCustomAttribute <DataMemberAttribute>(); if (dataMember != null) { return(true); } return (property.SetMethod != null && property.SetMethod.IsPublic && !property.SetMethod.IsStatic && property.SetMethod.GetParameters().Length == 1 && DeserializableMember.GetDefaultParser(property.SetMethod.GetParameters()[0].ParameterType.GetTypeInfo()) != null); }
private ManualTypeDescriberBuilder WithDeserializeMember(TypeInfo?forType, Setter?setter, string?name, Parser?parser, MemberRequired required, Reset?reset) { if (forType == null) { Throw.ArgumentNullException(nameof(forType)); } if (setter == null) { Throw.ArgumentNullException(nameof(setter)); } if (name == null) { Throw.ArgumentNullException(nameof(name)); } if (parser == null) { Throw.ArgumentNullException(nameof(parser)); } var toAdd = DeserializableMember.Create(forType, name, setter, parser, required, reset); if (!Deserializers.TryGetValue(forType, out var d)) { Deserializers[forType] = d = ImmutableArray.CreateBuilder <DeserializableMember>(); } d.Add(toAdd); return(this); }
/// <summary> /// Enumerate all columns to deserialize. /// </summary> public virtual IEnumerable <DeserializableMember> EnumerateMembersToDeserialize(TypeInfo forType) { var buffer = new List <(DeserializableMember Member, int?Position)>(); foreach (var p in forType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) { if (!ShouldDeserialize(forType, p)) { continue; } var name = GetDeserializationName(forType, p); var setter = GetSetter(forType, p); var parser = GetParser(forType, p); var order = GetPosition(forType, p); var isRequired = GetIsRequired(forType, p); var reset = GetReset(forType, p); buffer.Add((DeserializableMember.Create(forType, name, setter, parser, isRequired, reset), order)); } foreach (var f in forType.GetFields()) { if (!ShouldDeserialize(forType, f)) { continue; } var name = GetDeserializationName(forType, f); var parser = GetParser(forType, f); var order = GetPosition(forType, f); var isRequired = GetIsRequired(forType, f); var reset = GetReset(forType, f); buffer.Add((DeserializableMember.Create(forType, name, f, parser, isRequired, reset), order)); } buffer.Sort(TypeDescribers.DeserializableComparer); foreach (var(member, _) in buffer) { yield return(member); } }
/// <summary> /// Returns the default cell parsing method for the given type, if any. /// /// Override to tweak behavior. /// </summary> protected virtual MethodInfo GetCellParsingMethodFor(int columnNumber, string columName, TypeInfo targetType) => DeserializableMember.GetDefaultParser(targetType);
private static MethodInfo GetParser(TypeInfo forType) => DeserializableMember.GetDefaultParser(forType);
private static DeserializableMember Map(TypeInfo ontoType, DeserializableMember member) { MethodInfo?resetOnType = null; if (member.Reset.HasValue) { var surrogateResetWrapper = member.Reset.Value; if (surrogateResetWrapper.Mode != BackingMode.Method) { Throw.InvalidOperationException($"Cannot map reset {surrogateResetWrapper} onto {ontoType}, reset isn't backed by a method"); } var surrogateReset = surrogateResetWrapper.Method.Value; var surrogateResetBinding = GetEquivalentFlagsFor(surrogateReset.IsPublic, surrogateReset.IsStatic); Type[] resetTakesTypes; if (surrogateResetWrapper.TakesContext) { resetTakesTypes = new[] { Types.ReadContext.MakeByRefType() }; } else { resetTakesTypes = Type.EmptyTypes; } // intentionally letting this be null resetOnType = ontoType.GetMethod(surrogateReset.Name, surrogateResetBinding, null, resetTakesTypes, null); if (resetOnType == null) { Throw.InvalidOperationException($"No equivalent to {resetOnType} found on {ontoType}"); } } var surrogateSetterWrapper = member.Setter; switch (surrogateSetterWrapper.Mode) { case BackingMode.Field: { var surrogateField = surrogateSetterWrapper.Field.Value; var surrogateFieldBinding = GetEquivalentFlagsFor(surrogateField.IsPublic, surrogateField.IsStatic); // intentionally allowing null here var fieldOnType = ontoType.GetField(surrogateField.Name, surrogateFieldBinding); if (fieldOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateField} found on {ontoType}"); } if (fieldOnType.FieldType != surrogateField.FieldType) { Throw.InvalidOperationException($"Field {fieldOnType} type ({fieldOnType.FieldType}) does not match surrogate field {surrogateField} type ({surrogateField.FieldType})"); } var required = GetEquivalentRequiredFor(member.IsRequired); return(DeserializableMember.CreateInner(ontoType, member.Name, (Setter?)fieldOnType, member.Parser, required, (Reset?)resetOnType, null)); } case BackingMode.Method: { var surrogateSetter = surrogateSetterWrapper.Method.Value; var surrogateSetterBinding = GetEquivalentFlagsFor(surrogateSetter.IsPublic, surrogateSetter.IsStatic); // intentionally letting this be null var setterOnType = ontoType.GetMethod(surrogateSetter.Name, surrogateSetterBinding); if (setterOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateSetter} found on {ontoType}"); } var paramsOnType = setterOnType.GetParameters(); var paramsOnSurrogate = surrogateSetter.GetParameters(); if (paramsOnType.Length != paramsOnSurrogate.Length) { Throw.InvalidOperationException($"Parameters for {setterOnType} do not match parameters for {surrogateSetter}"); } for (var i = 0; i < paramsOnType.Length; i++) { var pOnType = paramsOnType[i]; var pOnSurrogate = paramsOnSurrogate[i]; if (pOnType.ParameterType != pOnSurrogate.ParameterType) { Throw.InvalidOperationException($"Parameter #{(i + 1)} on {setterOnType} does not match same parameter on {surrogateSetter}"); } } var required = GetEquivalentRequiredFor(member.IsRequired); return(DeserializableMember.CreateInner(ontoType, member.Name, (Setter?)setterOnType, member.Parser, required, (Reset?)resetOnType, null)); } case BackingMode.Delegate: Throw.InvalidOperationException($"Cannot map setter {surrogateSetterWrapper} onto {ontoType}, setter is backed by a delegate"); return(default); default: Throw.InvalidOperationException($"Unexpected {nameof(BackingMode)}: {surrogateSetterWrapper.Mode}"); return(default); } }
private static DeserializableMember Map(TypeInfo ontoType, DeserializableMember member) { MethodInfo resetOnType = null; if (member.Reset != null) { var surrogateReset = member.Reset; var surrogateResetBinding = // explicitly ignoring DeclaredOnly; shadowing is fine (surrogateReset.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) | (surrogateReset.IsStatic ? BindingFlags.Static : BindingFlags.Instance); resetOnType = ontoType.GetMethod(surrogateReset.Name, surrogateResetBinding); if (resetOnType == null) { Throw.InvalidOperationException($"No equivalent to {resetOnType} found on {ontoType}"); } } if (member.Field != null) { var surrogateField = member.Field; var surrogateFieldBinding = // explicitly ignoring DeclaredOnly; shadowing is fine (surrogateField.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) | (surrogateField.IsStatic ? BindingFlags.Static : BindingFlags.Instance); var fieldOnType = ontoType.GetField(surrogateField.Name, surrogateFieldBinding); if (fieldOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateField} found on {ontoType}"); } if (fieldOnType.FieldType != surrogateField.FieldType) { Throw.InvalidOperationException($"Field {fieldOnType} type ({fieldOnType.FieldType}) does not match surrogate field {surrogateField} type ({surrogateField.FieldType})"); } return(DeserializableMember.Create(ontoType, member.Name, fieldOnType, member.Parser, member.IsRequired, resetOnType)); } var surrogateSetter = member.Setter; var surrogateSetterBinding = // explicitly ignoring DeclaredOnly; shadowing is fine (surrogateSetter.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) | (surrogateSetter.IsStatic ? BindingFlags.Static : BindingFlags.Instance); var setterOnType = ontoType.GetMethod(surrogateSetter.Name, surrogateSetterBinding); if (setterOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateSetter} found on {ontoType}"); } var paramsOnType = setterOnType.GetParameters(); var paramsOnSurrogate = surrogateSetter.GetParameters(); if (paramsOnType.Length != paramsOnSurrogate.Length) { Throw.InvalidOperationException($"Parameters for {setterOnType} do not match parameters for {surrogateSetter}"); } for (var i = 0; i < paramsOnType.Length; i++) { var pOnType = paramsOnType[i]; var pOnSurrogate = paramsOnSurrogate[i]; if (pOnType.ParameterType != pOnSurrogate.ParameterType) { Throw.InvalidOperationException($"Parameter #{(i + 1)} on {setterOnType} does not match same parameter on {surrogateSetter}"); } } return(DeserializableMember.Create(ontoType, member.Name, setterOnType, member.Parser, member.IsRequired, resetOnType)); }
/// <summary> /// Enumerate members which will be deserialized for the given type. /// /// Note that these members are generated ahead of time with a source gneerator, /// and cannot be changed at runtime. /// </summary> public IEnumerable <DeserializableMember> EnumerateMembersToDeserialize(TypeInfo forType) { var paired = GetPairedType(forType, DESERIALIZER_KIND); if (paired == null) { return(Enumerable.Empty <DeserializableMember>()); } var colNamesProp = paired.GetPropertyNonNull("__ColumnNames", PublicStatic); var colNames = (ImmutableArray <string>)colNamesProp.GetValueNonNull(null); var ret = ImmutableArray.CreateBuilder <DeserializableMember>(colNames.Length); ParameterInfo[]? consPs = null; for (var i = 0; i < colNames.Length; i++) { var name = colNames[i]; var colReaderName = $"__Column_{i}"; var colReaderMtd = paired.GetMethodNonNull(colReaderName, PublicInstance); #pragma warning disable CS0618 // These are obsolete to prevent clients from using them, but they are fine for us. var isRequired = colReaderMtd.GetCustomAttribute <IsRequiredAttribute>() != null; var setterBackedByParameter = colReaderMtd.GetCustomAttribute <SetterBackedByConstructorParameterAttribute>(); var setterIsInitOnly = colReaderMtd.GetCustomAttribute <SetterBackedByInitOnlyPropertyAttribute>(); #pragma warning restore CS0618 Setter setter; if (setterBackedByParameter == null && setterIsInitOnly == null) { // directly a method var setterName = $"__Column_{i}_Setter"; var setterMtd = paired.GetMethodNonNull(setterName, PublicStatic); setter = Setter.ForMethod(setterMtd); } else if (setterBackedByParameter != null) { // parameter to constructor if (consPs == null) { var ip = GetInstanceProvider(forType); ip = Utils.NonNull(ip); var cons = ip.Constructor.Value; consPs = cons.GetParameters(); } var consParameterIndex = setterBackedByParameter.Index; if (consParameterIndex < 0 || consParameterIndex >= consPs.Length) { Throw.ImpossibleException($"Setter for column {i} claims to be backed by constructor parameter, but its position is out of bounds (index={consParameterIndex})"); } var p = consPs[setterBackedByParameter.Index]; setter = Setter.ForConstructorParameter(p); } else { // init only property var initOnly = Utils.NonNull(setterIsInitOnly); var prop = forType.GetProperty(initOnly.PropertyName, initOnly.BindingFlags); if (prop == null) { Throw.ImpossibleException($"Setter for column {i} claims to be backed by init-only property {initOnly.PropertyName} with bindings ({initOnly.BindingFlags}), but it could not be found"); } setter = Setter.ForProperty(prop); } var resetMethodName = $"__Column_{i}_Reset"; var resetMtd = paired.GetMethod(resetMethodName, PublicStatic); var reset = (Reset?)resetMtd; var parserMethodName = $"__Column_{i}_Parser"; var parserMtd = paired.GetMethod(parserMethodName, PublicStatic); Parser?parser; if (parserMtd != null) { parser = Parser.ForMethod(parserMtd); } else { parser = Utils.NonNull(Parser.GetDefault(setter.Takes)); } ret.Add(DeserializableMember.CreateInner(forType, name, setter, parser, isRequired ? MemberRequired.Yes : MemberRequired.No, reset, paired)); } return(ret.ToImmutable()); }