Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSHARP-4495: Add conventions and attributes to configure ObjectSerial… #1545

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

papafe
Copy link
Contributor

@papafe papafe commented Nov 15, 2024

…izer AllowedTypes

@papafe
Copy link
Contributor Author

papafe commented Nov 15, 2024

This is an incredibly quick and dirty proof of concept for a new convention that allows users to define the allowed types for serialization/deserialization in multiple ways:

  • passing a delegate
  • passing a list of types
  • passing a list of accepted assemblies
  • passing nothing, and this would be the same as passing the calling assembly

I have also thought if it would make sense to give users the possibility of setting the allowed types with attributes, but I suppose that most of the times that list of types would be valid for the whole project, not only for certain POCOs.

As said, this just a very fast POC, so there's a lot missing, but it's to get some initial feedback.

Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks very reasonable!

/// <param name="allowedSerializationTypes"></param>
public AllowedTypesConvention(IEnumerable<Type> allowedDeserializationTypes, IEnumerable<Type> allowedSerializationTypes)
{
var allowedDeserializationTypesArray = allowedDeserializationTypes as Type[] ?? allowedDeserializationTypes.ToArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting optimization...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However... we should probably make a defensive copy anyway because the caller might alter the array they passed in later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should just make a copy

///
/// </summary>
#pragma warning disable CA1044
public bool AllowDefaultFrameworkTypes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of giving them a way to opt-in the default framework types.

@papafe papafe marked this pull request as ready for review November 18, 2024 10:35
@papafe papafe requested a review from a team as a code owner November 18, 2024 10:35
@papafe papafe requested a review from rstam November 18, 2024 10:35
@papafe
Copy link
Contributor Author

papafe commented Nov 18, 2024

@rstam This is ready for review now. Apart from all your notes, I've also allowed the convention to work with collections and nested collection (as we did with the EnumConvention).

Copy link
Contributor

@BorisDog BorisDog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall

/// Initializes a new instance of the <see cref="ObjectSerializerAllowedTypesConvention"/> class
/// that allows all types contained in the calling assembly.
/// </summary>
public ObjectSerializerAllowedTypesConvention() : this(Assembly.GetCallingAssembly())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should define that the default behavior does not configure any allowed types (except for DefaultFrameworkAllowedTypes). So the configuration needs to be explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the automatic add of the types in the calling assembly and I have made AllowDefaultFrameworkTypes default to true, similar to the behaviour of the ObjectSerializer. For this reason now the delegates that are passed to the ObjectSerializer are built lazily.

@papafe papafe requested a review from BorisDog November 20, 2024 12:41
? ObjectSerializer.DefaultAllowedTypes
: t => _allowedDeserializationTypes(t) || ObjectSerializer.DefaultAllowedTypes(t)
: _allowedDeserializationTypes ?? ObjectSerializer.NoAllowedTypes;
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope that this is readable enough

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine. minor optimization possible:

_lazyAllowedDeserializationTypes = new Lazy<Func<Type, bool>>(() => Construct(_allowedDeserializationTypes));
_lazyAllowedSerializationTypes= new Lazy<Func<Type, bool>>(() => Construct(_allowedSerializationTypes));

Func<Type, bool> Construct(Func<Type, bool> allowedTypes) => AllowDefaultFrameworkTypes
    ? (allowedTypes != null ? t => allowedTypes(t) || ObjectSerializer.DefaultAllowedTypes) : ObjectSerializer.DefaultAllowedTypes
    : allowedTypes ?? ObjectSerializer.NoAllowedTypes;

? ObjectSerializer.DefaultAllowedTypes
: t => _allowedDeserializationTypes(t) || ObjectSerializer.DefaultAllowedTypes(t)
: _allowedDeserializationTypes ?? ObjectSerializer.NoAllowedTypes;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine. minor optimization possible:

_lazyAllowedDeserializationTypes = new Lazy<Func<Type, bool>>(() => Construct(_allowedDeserializationTypes));
_lazyAllowedSerializationTypes= new Lazy<Func<Type, bool>>(() => Construct(_allowedSerializationTypes));

Func<Type, bool> Construct(Func<Type, bool> allowedTypes) => AllowDefaultFrameworkTypes
    ? (allowedTypes != null ? t => allowedTypes(t) || ObjectSerializer.DefaultAllowedTypes) : ObjectSerializer.DefaultAllowedTypes
    : allowedTypes ?? ObjectSerializer.NoAllowedTypes;

/// <summary>
/// A convention that allows to set the types that can be safely serialized and deserialized with the <see cref="ObjectSerializer"/>.
/// </summary>
public sealed class ObjectSerializerAllowedTypesConvention
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have convenience presets, like:

ObjectSerializerAllowedTypesConvention.AllowAllTypes
ObjectSerializerAllowedTypesConvention.AllowAllExecutingAssemblyTypes
ObjectSerializerAllowedTypesConvention.DefaultAllowedTypes
...

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would make sense

/// <summary>
/// Default <see cref="ObjectSerializerAllowedTypesConvention"/> where all calling assembly types and default framework types are allowed for both serialization and deserialization.
/// </summary>
public static ObjectSerializerAllowedTypesConvention AllowAllCallingAssemblyAndDefaultFrameworkTypes => new(Assembly.GetCallingAssembly());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it makes sense to have both AllowAllCallingAssemblyAndDefaultFrameworkTypes and AllowAllCallingAssemblyTypes or keep only the second one (but then we need to decide if the default framework types are included or not)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need only need AllowAllCallingAssemblyAndDefaultFrameworkTypes

@papafe papafe requested a review from BorisDog November 21, 2024 07:45
/// <summary>
/// Default <see cref="ObjectSerializerAllowedTypesConvention"/> where all calling assembly types and default framework types are allowed for both serialization and deserialization.
/// </summary>
public static ObjectSerializerAllowedTypesConvention AllowAllCallingAssemblyAndDefaultFrameworkTypes => new(Assembly.GetCallingAssembly());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need only need AllowAllCallingAssemblyAndDefaultFrameworkTypes

{
_lazyAllowedDeserializationTypes = new Lazy<Func<Type, bool>>(() => Construct(_allowedDeserializationTypes));
_lazyAllowedSerializationTypes = new Lazy<Func<Type, bool>>(() => Construct(_allowedSerializationTypes));
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for return

public static ObjectSerializerAllowedTypesConvention AllowAllTypes => new(ObjectSerializer.AllAllowedTypes);

/// <summary>
/// Default <see cref="ObjectSerializerAllowedTypesConvention"/> where no types are allowed for both serialization and deserialization.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not need for the word "Default", as it might be confused for an actual default?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants