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

Support mapping derived types #330

Closed
onionhammer opened this issue Apr 13, 2023 · 16 comments · Fixed by #366
Closed

Support mapping derived types #330

onionhammer opened this issue Apr 13, 2023 · 16 comments · Fixed by #366
Assignees
Labels
enhancement New feature or request

Comments

@onionhammer
Copy link

onionhammer commented Apr 13, 2023

[KnownType] and [JsonDerivedType] attributes are used on a base type to inform callers/serializers etc what its known derived types are, Mapperly could use this to automatically generate bindings for known types

@latonz
Copy link
Contributor

latonz commented Apr 13, 2023

As stated by the MS docs

Specifies types that should be recognized by the DataContractSerializer when serializing or deserializing a given type.
this is used by DataContractSerializer. Mapperly should not be coupled to the DataContractSerializer.

Could you provide an example of a mapper declaration including relevant classes and what you expect the generated code to look like?

@onionhammer
Copy link
Author

onionhammer commented Apr 13, 2023

@latonz KnownType is also used by Swagger to find derived types.

.NET's new [JsonDerivedType] attribute could also be used as an attribute to detect derived types.

Usage could look like this

[KnownType(typeof(Dog))]
[KnownType(typeof(Cat))]
public abstract class AnimalBase
{
    public required string Name { get; set; }
}

public class Dog : AnimalBase
{
    public required string DogName { get; set; }
}

public class Cat : AnimalBase
{
    public int CatAge { get; set; }
}

public abstract class AnimalBaseDTO
{
    public string? Name { get; set; }
}

public class DogDTO : AnimalBaseDTO
{
    public string? DogName { get; set; }
}

public class CatDTO : AnimalBaseDTO
{
    public int CatAge { get; set; }
}

public static partial class Mapper
{
    public static partial List<AnimalBaseDTO>? MapAnimal(List<AnimalBase>? input);
}

Mapperly's automatic implementation could look like this

public static partial class ModelMapper
{
    public static partial List<AnimalBaseDTO>? MapAnimal(List<AnimalBase>? input)
    {
        if (input == null)
            return null;

        return new List<AnimalBaseDTO>(
            input.Select(p => p switch
            {
                Dog dog => MapDog(dog),
                Cat cat => MapCat(cat),
                _ => throw new NotSupportedException(),
            })
        );
    }

    public static MapDog(Dog input)
    {
        return new CatDto
        {
            Name = input.Name,
            DogName = input.DogName,
        };
    }

    public static MapCat(Cat input)
    {
        return new DogDto
        {
            Name = input.Name,
            CatAge = input.CatAge,
        };
    }
}

@latonz latonz added the enhancement New feature or request label Apr 13, 2023
@latonz
Copy link
Contributor

latonz commented Apr 13, 2023

Ah now I understand what you are looking for. This would be a great addition to Mapperly!
However, a separate attribute will probably be necessary to match type pairs (source type and associated target type, Dog => DogDto in your sample). This could look like this:

[Mapper]
public static partial class ModelMapper
{
    public static partial List<AnimalBaseDTO> MapAnimals(List<AnimalBase> input);

    [MapDerivedType(typeof(Dog), typeof(DogDto))]
    [MapDerivedType(typeof(Cat), typeof(CatDto))]
    private static partial AnimalBaseDTO MapAnimal(AnimalBase input);
}

@latonz latonz changed the title Automatically handle KnownTypeAttribute Support mapping derived types Apr 13, 2023
@onionhammer

This comment was marked as resolved.

@latonz

This comment was marked as resolved.

@onionhammer

This comment was marked as resolved.

@latonz

This comment was marked as resolved.

@onionhammer

This comment was marked as resolved.

@latonz

This comment was marked as resolved.

@onionhammer

This comment was marked as resolved.

@latonz

This comment was marked as resolved.

@onionhammer

This comment was marked as resolved.

@latonz

This comment was marked as resolved.

@latonz latonz self-assigned this Apr 18, 2023
@jnyrup
Copy link

jnyrup commented Apr 19, 2023

A perhaps simpler case is if using Mapperly to generate a deep cloner.
Wouldn't TSource then trivially be equal to TDest?

(You might want to hide the comments regarding generic attributes to keep the thread manageable)

@latonz
Copy link
Contributor

latonz commented Apr 19, 2023

@jnyrup that's true. I don't think it is worth to introduce a separate attribute for this, as Mapperly's primary use case is object-to-object mapping and the verbosity in this case isn't much higher to configure Mapperly to just clone objects.

@github-actions
Copy link

github-actions bot commented Aug 7, 2023

🎉 This issue has been resolved in version 3.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Successfully merging a pull request may close this issue.

3 participants