Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@Kataane
Copy link

@Kataane Kataane commented Oct 30, 2025

Add SnakeCase Property Name Mapping Strategy

Description

This PR adds support for SnakeCase as a new property name mapping strategy in Mapperly, enabling automatic mapping between PascalCase properties in C# source objects and snake_case properties in target objects.

Motivation

In modern C# development, it's common to work with external systems or code generators that produce DTOs with snake_case property names (e.g., from JSON APIs, database schemas, or code generation tools). Currently, mapping between C# PascalCase properties and snake_case properties requires manual [MapProperty] attributes for each property, which is tedious and error-prone for large objects.

This feature is particularly valuable when:

  • Working with auto-generated C# DTOs from external contracts that use snake_case naming
  • Integrating with APIs that follow snake_case conventions (common in Python, Ruby, or REST APIs)
  • The source contracts cannot be modified due to external dependencies or architectural constraints

Changes Made

  • Added SnakeCase enum value to PropertyNameMappingStrategy with XML documentation
  • Implemented snake_case to PascalCase conversion for property matching in MemberPathCandidateBuilder
  • Updated MemberPathCandidateBuilder with new overload supporting naming strategies
  • Modified MembersMappingBuilderContext to use SnakeCase strategy when configured
  • Updated PublicAPI snapshot to reflect new enum value
  • Added comprehensive test case for SnakeCase property mapping validation

Usage Example

[Mapper(PropertyNameMappingStrategy = PropertyNameMappingStrategy.SnakeCase)]
public partial class MyMapper
{
    public partial TargetDto Map(SourceDto source);
}

// Maps automatically:
// source.FirstName -> target.first_name
// source.LastName -> target.last_name

This allows developers to map properties between PascalCase source objects and snake_case target objects (e.g., FirstName -> first_name) by simply setting PropertyNameMappingStrategy = PropertyNameMappingStrategy.SnakeCase.

Fixes # (issue - if applicable)

Checklist

  • The existing code style is followed
  • The commit message follows our guidelines
  • Performed a self-review of my code
  • Hard-to-understand areas of my code are commented
  • The documentation is updated (as applicable)
  • Unit tests are added/updated
  • Integration tests are added/updated (as applicable, especially if feature/bug depends on roslyn or framework version in use)

Kataane and others added 3 commits October 30, 2025 20:02
- Added SnakeCase enum value to PropertyNameMappingStrategy with XML documentation
- Implemented snake_case to PascalCase conversion for property matching
- Updated MemberPathCandidateBuilder with new overload supporting naming strategies
- Modified MembersMappingBuilderContext to use SnakeCase strategy when configured
- Updated PublicAPI snapshot to reflect new enum value
- Added test case for SnakeCase property mapping validation

This allows developers to map properties between PascalCase source objects
and snake_case target objects (e.g., FirstName -> first_name) by setting
PropertyNameMappingStrategy = PropertyNameMappingStrategy.SnakeCase
Copy link
Contributor

@latonz latonz left a comment

Choose a reason for hiding this comment

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

Thank you for this contribution. I added my feedback. IMO we should also support auto flattening (see docs) and UpperSnakeCase.

@latonz latonz added the enhancement New feature or request label Oct 31, 2025
@latonz
Copy link
Contributor

latonz commented Oct 31, 2025

Please also update the documentation.

@Kataane
Copy link
Author

Kataane commented Dec 6, 2025

Hi @latonz,
Could you please review the changes? I would like to add this functionality to my code as soon as possible.

Copy link
Contributor

@latonz latonz left a comment

Choose a reason for hiding this comment

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

Thanks for the updates!

Comment on lines 222 to 227
private static List<string> BuildPermutationList(string source, int[] indices, int indicesCount, int mask, bool skipDelimiter)
{
// Pre-calculate exact capacity to avoid internal List resizing
// 1 base segment + 1 for every split
var capacity = 1 + CountSetBits(mask);
var list = new List<string>(capacity);
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like you attempted to optimize for performance. Have you benchmarked this yet?
From my perspective the previous code was easier to follow, and I might be biased, so I would still like to double check whether the added complexity is justified.
There is probably still room for significant optimization by using (ReadOnly)Spans, stackalloc and/or array pools. I'm not sure whether we should move these performance optimizations into a separate PR.

Copy link
Author

Choose a reason for hiding this comment

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

It looks like you attempted to optimize for performance

Yes and no. The main goal was to unify the BuildPermutationList method. I wanted it to work for any notation, so I didn't focus much on optimization, such as using (ReadOnly)Spans, stackalloc and/or array pools.

I was wondering, though, if there will be any other notations in the future. KebabCase is not possible as a notation in C#.

If you are confused by this code, I can revert it.

@Kataane
Copy link
Author

Kataane commented Dec 14, 2025

Hi @latonz,

Thank you for your review.

I fixed the issues based on your reviews. The main point that is still unresolved is the complex login in MemberPathCandidateBuilder.cs.

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 this pull request may close these issues.

2 participants