eiriktsarpalis / PolyType

Practical generic programming for .NET
https://eiriktsarpalis.github.io/PolyType/
MIT License
154 stars 8 forks source link

Allow applying naming policy to properties #44

Open AArnott opened 2 weeks ago

AArnott commented 2 weeks ago

From this conversation @eiriktsarpalis said:

I'm thinking that the concept of applying a naming policy is something that transfers to shapes in general, so perhaps it makes sense to lift that to being a feature in the core library.

I was thinking the same thing.

AArnott commented 2 weeks ago

Once you have naming policy support, it would be great PolyType could offer a map of properties and constructor parameters, or (even simpler) the properties themselves should have something more than HasSetter that would indicate whether the constructor allows indirectly setting the property.

The reason for this is that I don't want a serializer to include serialization of a property that has only a getter and no constructor parameter either.

eiriktsarpalis commented 2 weeks ago

it would be great PolyType could offer a map of properties and constructor parameters, or (even simpler) the properties themselves should have something more than HasSetter that would indicate whether the constructor allows indirectly setting the property.

The parameters of constructor shapes already includes settable members modulo those matching constructor parameters.

Beyond that though, the library doesn't try to explicitly pair properties to parameters or fail if no match is found. System.Text.Json does do this fairly aggressively and we've received feedback from users that this is undesirable in scenaria where you only care about serialization or deserialization but not about being able to roundtrip values. This feels like a policy that can be enforced by individual consumers.

AArnott commented 2 weeks ago

Beyond that though, the library doesn't try to explicitly pair properties to parameters or fail if no match is found.

Let's put the "fail if not match is found" or skipping serialization on no match aside for a moment.

I still have to (at present) write the code to match a serialized property name to a constructor parameter. This is a pain, and requires that I decide the policy for how user's constructor parameter names must match the property name. Consider this case:

class Foo
{
   public Foo(int a);

   public int A { get; }
}

By default, this might serialize to JSON like this:

{ "A": 5 }

Now, upon _de_serialization, how should the deserializer decide what to do with the JSON A property? It doesn't match the a constructor parameter. So should it drop it, or should it decide based on a camelCase/PascalCase match, or should it be totally case-insensitive?

From a PolyType perspective, we have an opportunity perhaps to unify how serializers work including potentially some of their policies. Personally, for a fresh MessagePack library such as the one I'm building, I'd rather not have to decide this policy and rely on a common framework like PolyType to define it, so that users can define their types once with PolyType conformance, and then switch from serializing to JSON to MsgPack to Cbor -- and have it all Just Work instead of one of those formats failing because they don't know that property A should be matched with ctor parameter a.

eiriktsarpalis commented 2 weeks ago

Now, upon _de_serialization, how should the deserializer decide what to do with the JSON A property? It doesn't match the a constructor parameter. So should it drop it, or should it decide based on a camelCase/PascalCase match, or should it be totally case-insensitive?

These are all valid options. I don't believe this association should be the purview of shapes but a matter of policy in individual serializers. We can of course choose to expose helpers that implement either of these policies in a streamlined fashion.

eiriktsarpalis commented 2 weeks ago

I'd rather not have to decide this policy and rely on a common framework like PolyType to define it, so that users can define their types once with PolyType conformance, and then switch from serializing to JSON to MsgPack to Cbor

I agree that this would be a desirable goal. At some point we could consider implementing something like Serde.NET on top of PolyType using a common set of conventions. Adding support for a new format would be as simple as implementing a pair of reader/writer classes.

AArnott commented 1 week ago

Another use case for consistent mapping from constructor parameters to properties: https://github.com/AArnott/Nerdbank.MessagePack/issues/89 needs to decide which property is impacted by the default value as specified as a default parameter value in a primary constructor so that we can decide whether to serialize that property.