// property serialization defaults /// <summary> /// Returns true if the given property should be serialized. /// /// Override to tweak behavior. /// </summary> protected virtual bool ShouldSerialize(TypeInfo forType, PropertyInfo property) { if (property.GetMethod == 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.GetMethod.IsPublic && !property.GetMethod.IsStatic && property.GetMethod.GetParameters().Length == 0 && property.GetMethod.ReturnType != Types.VoidType && SerializableMember.GetDefaultFormatter(property.GetMethod.ReturnType.GetTypeInfo()) != null); }
private static SerializableMember Map(TypeInfo ontoType, SerializableMember member) { ShouldSerialize?shouldSerializeOnType; if (member.ShouldSerialize.HasValue) { var surrogateShouldSerializeWrapper = member.ShouldSerialize.Value; if (surrogateShouldSerializeWrapper.Mode == BackingMode.Method) { if (surrogateShouldSerializeWrapper.Takes.HasValue && surrogateShouldSerializeWrapper.IsStatic) { Throw.InvalidOperationException($"Cannot map 'should serialize' {surrogateShouldSerializeWrapper} onto {ontoType}, it takes a parameter"); } var surrogateShouldSerialize = surrogateShouldSerializeWrapper.Method.Value; var surrogateShouldSerializeBinding = GetEquivalentFlagsFor(surrogateShouldSerialize.IsPublic, surrogateShouldSerialize.IsStatic); // intentionally letting this be null var shouldSerializeOnTypeMtd = ontoType.GetMethod(surrogateShouldSerialize.Name, surrogateShouldSerializeBinding); if (shouldSerializeOnTypeMtd == null) { Throw.InvalidOperationException($"No equivalent to {surrogateShouldSerialize} found on {ontoType}"); } shouldSerializeOnType = ShouldSerialize.ForMethod(shouldSerializeOnTypeMtd); } else { Throw.InvalidOperationException($"Cannot map 'should serialize' {surrogateShouldSerializeWrapper} onto {ontoType}, 'should serialize' isn't backed by a method"); return(default);
// internal for testing purposes internal ManualTypeDescriberBuilder WithSerializableMember(TypeInfo?forType, Getter?getter, string?name, Formatter?formatter, ShouldSerialize?shouldSerialize, EmitDefaultValue emitDefault) { if (forType == null) { Throw.ArgumentNullException(nameof(forType)); } if (getter == null) { Throw.ArgumentNullException(nameof(getter)); } if (name == null) { Throw.ArgumentNullException(nameof(name)); } if (formatter == null) { Throw.ArgumentNullException(nameof(name)); } // shouldSerialize can be null if (getter.RowType.HasValue) { var getterOnType = getter.RowType.Value; var isLegal = false; TypeInfo?cur = forType; while (cur != null) { if (cur == getterOnType) { isLegal = true; break; } cur = cur?.BaseType?.GetTypeInfo(); } if (!isLegal) { Throw.InvalidOperationException($"Provided getter ({getter}) is not on {forType} or one of it's base types."); } } var toAdd = SerializableMember.Create(forType, name, getter, formatter, shouldSerialize, emitDefault); if (!Serializers.TryGetValue(forType, out var s)) { Serializers[forType] = s = ImmutableArray.CreateBuilder <SerializableMember>(); } s.Add(toAdd); return(this); }
/// <summary> /// Enumerate members which will be serialized 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 <SerializableMember> EnumerateMembersToSerialize(TypeInfo forType) { var paired = GetPairedType(forType, SERIALIZER_KIND); if (paired == null) { return(Enumerable.Empty <SerializableMember>()); } var colNamesField = paired.GetFieldNonNull("ColumnNames", PublicStatic); var colNames = (ImmutableArray <string>)colNamesField.GetValueNonNull(null); var ret = ImmutableArray.CreateBuilder <SerializableMember>(colNames.Length); for (var i = 0; i < colNames.Length; i++) { var name = colNames[i]; var colWriterName = $"__Column_{i}"; var colWriterMtd = paired.GetMethodNonNull(colWriterName, PublicStatic); #pragma warning disable CS0618 // This is obsolete to prevent clients from using them, but they are fine for us. var emitsDefaultValue = colWriterMtd.GetCustomAttribute <DoesNotEmitDefaultValueAttribute>() == null; #pragma warning restore CS0618 var shouldSerializeName = $"__Column_{i}_ShouldSerialize"; var shouldSerializeMtd = paired.GetMethod(shouldSerializeName, PublicStatic); var shouldSerialize = (ShouldSerialize?)shouldSerializeMtd; var getterName = $"__Column_{i}_Getter"; var getterMtd = paired.GetMethodNonNull(getterName, PublicStatic); var getter = Getter.ForMethod(getterMtd); var formatterName = $"__Column_{i}_Formatter"; var formatterMtd = paired.GetMethod(formatterName, PublicStatic); Formatter formatter; if (formatterMtd == null) { // if a method isn't provided, it must be using the default formatter = Utils.NonNull(Formatter.GetDefault(getter.Returns)); } else { formatter = Formatter.ForMethod(formatterMtd); } ret.Add(SerializableMember.ForGeneratedMethod(name, colWriterMtd, getter, formatter, shouldSerialize, emitsDefaultValue)); } return(ret.ToImmutable()); }
/// <summary> /// Enumerate all columns to deserialize. /// </summary> public virtual IEnumerable <SerializableMember> EnumerateMembersToSerialize(TypeInfo forType) { var buffer = new List <(SerializableMember Member, int?Position)>(); foreach (var p in forType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) { if (!ShouldSerialize(forType, p)) { continue; } var name = GetSerializationName(forType, p); var getter = GetGetter(forType, p); var shouldSerialize = GetShouldSerializeMethod(forType, p); var formatter = GetFormatter(forType, p); var order = GetPosition(forType, p); var emitDefault = GetEmitDefaultValue(forType, p); buffer.Add((SerializableMember.Create(forType, name, getter, formatter, shouldSerialize, emitDefault), order)); } foreach (var f in forType.GetFields()) { if (!ShouldSerialize(forType, f)) { continue; } var name = GetSerializationName(forType, f); var shouldSerialize = GetShouldSerializeMethod(forType, f); var formatter = GetFormatter(forType, f); var order = GetPosition(forType, f); var emitDefault = GetEmitDefaultValue(forType, f); buffer.Add((SerializableMember.Create(forType, name, f, formatter, shouldSerialize, emitDefault), order)); } buffer.Sort(TypeDescribers.SerializableComparer); foreach (var(member, _) in buffer) { yield return(member); } }
// common serialization defaults private static MethodInfo GetFormatter(TypeInfo t) => SerializableMember.GetDefaultFormatter(t);
private static SerializableMember Map(TypeInfo ontoType, SerializableMember member) { MethodInfo shouldSerializeOnType; if (member.ShouldSerialize != null) { var surrogateShouldSerialize = member.ShouldSerialize; var surrogateShouldSerializeBinding = // explicitly ignoring DeclaredOnly; shadowing is fine (surrogateShouldSerialize.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) | (surrogateShouldSerialize.IsStatic ? BindingFlags.Static : BindingFlags.Instance); shouldSerializeOnType = ontoType.GetMethod(surrogateShouldSerialize.Name, surrogateShouldSerializeBinding); if (shouldSerializeOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateShouldSerialize} found on {ontoType}"); } } else { shouldSerializeOnType = null; } 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(SerializableMember.Create(ontoType, member.Name, fieldOnType, member.Formatter, shouldSerializeOnType, member.EmitDefaultValue)); } var surrogateGetter = member.Getter; var surrogateGetterBinding = // explicitly ignoring DeclaredOnly; shadowing is fine (surrogateGetter.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) | (surrogateGetter.IsStatic ? BindingFlags.Static : BindingFlags.Instance); var getterOnType = ontoType.GetMethod(surrogateGetter.Name, surrogateGetterBinding); if (getterOnType == null) { Throw.InvalidOperationException($"No equivalent to {surrogateGetter} found on {ontoType}"); } var surrogateParams = surrogateGetter.GetParameters(); var onTypeParams = getterOnType.GetParameters(); if (surrogateParams.Length != onTypeParams.Length) { Throw.InvalidOperationException($"Parameters for {getterOnType} do not match parameters for {surrogateGetter}"); } for (var i = 0; i < surrogateParams.Length; i++) { var sP = surrogateParams[i].ParameterType.GetTypeInfo(); var tP = onTypeParams[i].ParameterType.GetTypeInfo(); if (sP != tP) { Throw.InvalidOperationException($"Parameter #{(i + 1)} on {getterOnType} does not match same parameter on {surrogateGetter}"); } } return(SerializableMember.Create(ontoType, member.Name, getterOnType, member.Formatter, shouldSerializeOnType, member.EmitDefaultValue)); }