Repository navigation
Flattening ignores explicit type mapping rules #980
Copy link
Copy link
Open
Labels
Description
Setup
I have an Entity object with Props collection.
class Entity
{
public string Name { get; set; } = default!;
public List<EntityProp> Props { get; set; } = default!;
}
class EntityProp
{
public string Key { get; set; } = default!;
public string Value { get; set; } = default!;
}My goal is to map it into a Dto object where items from Props collections become properties of destination object.
class Dto
{
public string Name { get; set; } = default!;
public string? Address { get; set; }
public string? Description { get; set; }
public string? Phone { get; set; }
}For versatility I have two mapping. First is to flatten the Props into destination root and second to configure Props mapping.
config.NewConfig<Entity, Dto>().Map(e => e, e => e.Props);
config.NewConfig<List<EntityProp>, Dto>()
.Map(e => e.Address, e => e.Get("Address"))
.Map(e => e.Description, e => e.Get("Description"))
.Map(e => e.Phone, e => e.Get("Phone"));Problem
Second mapping rules are not obeyed during conversion. Is this pattern valid? Any workaround?
Complete code sample
using System.Collections.Generic;
using System.Linq;
using Mapster;
class Entity
{
public string Name { get; set; } = default!;
public List<EntityProp> Props { get; set; } = default!;
}
class EntityProp
{
public string Key { get; set; } = default!;
public string Value { get; set; } = default!;
}
class Dto
{
public string Name { get; set; } = default!;
public string? Address { get; set; }
public string? Description { get; set; }
public string? Phone { get; set; }
}
static class TestMapster
{
public static void Test()
{
TypeAdapterConfig config = new();
config.NewConfig<Entity, Dto>().Map(e => e, e => e.Props);
config.NewConfig<List<EntityProp>, Dto>()
.Map(e => e.Address, e => e.Get("Address"))
.Map(e => e.Description, e => e.Get("Description"))
.Map(e => e.Phone, e => e.Get("Phone"));
Entity ent = new()
{
Name = "My entity",
Props = [
new() { Key = "Phone", Value = "12345678" }
],
};
Dto dto = ent.Adapt<Dto>(config);
// dto.Name is mapped correctly and equals to "My entity".
// Expected dto.Phone to be equal to "12345678",
// but it is not mapped and is null
}
}
static class ListExtensions
{
// utility method as expression bodies are not allowed to have null propagating operator
public static string? Get(this List<EntityProp> list, string key)
=> list.FirstOrDefault(e => e.Key == key)?.Value;
}Reactions are currently unavailable
Activity
dcby commentedon May 28, 2026
The opposite (unflattening) ignores configuration too. I.e.
feat(test): add revers type mapping test for MapsterMapper#980
DocSvartz commentedon May 28, 2026
Hi, @dcby this is more likely Toplevel mapping from two sources at once, and apparently it was not supported for Custom mappings (Map). Flattening/Unflattening In my opinion, it's a little different..
@dcby What version of mapster are you using?
I think I've found a solution for at least this case, and it will be available in the next version of Mapster.
Now for your cases you need to use
AfterMapping()DocSvartz commentedon May 28, 2026
// First Source
Entity ent = new()
dcby commentedon May 28, 2026
@DocSvartz,
It's 10.0.7 (the latest to the moment)
Yes, this is exactly what I'm doing now for both cases. But it feels "unnatural".
Thank you!
dcby commentedon May 28, 2026
Let's call it Merging/Unmerging. Would be great to have first class support for it.