Skip to content

NamingConventions

Arnel Robles edited this page Oct 2, 2026 · 2 revisions

Package 5: Mapsicle.NamingConventions

Automatic naming convention conversion, map between snake_case, PascalCase, camelCase, and kebab-case!

Basic Usage

using Mapsicle.NamingConventions;

// Source uses snake_case (e.g., from Python API or database)
public class ApiResponse
{
    public int user_id { get; set; }
    public string first_name { get; set; }
    public string email_address { get; set; }
}

// Destination uses PascalCase (C# convention)
public class UserDto
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string EmailAddress { get; set; }
}

// Map with naming convention conversion
var dto = apiResponse.MapWithConvention<ApiResponse, UserDto>(
    NamingConvention.SnakeCase,
    NamingConvention.PascalCase);

// dto.UserId == apiResponse.user_id
// dto.FirstName == apiResponse.first_name

Built-in Conventions

Convention Example C# Property
NamingConvention.PascalCase UserName Standard C#
NamingConvention.CamelCase userName JavaScript/JSON
NamingConvention.SnakeCase user_name Python/Ruby/SQL
NamingConvention.KebabCase user-name URLs/CSS

Convert Property Names

// Convert a single name
var snake = "UserName".ConvertName(NamingConvention.PascalCase, NamingConvention.SnakeCase);
// Result: "user_name"

var pascal = "first_name".ConvertName(NamingConvention.SnakeCase, NamingConvention.PascalCase);
// Result: "FirstName"

var camel = "OrderCount".ConvertName(NamingConvention.PascalCase, NamingConvention.CamelCase);
// Result: "orderCount"

Use with Fluent Mapper

// Combine with IMapper for convention-based mapping
var dto = mapper.MapWithConvention<ApiResponse, UserDto>(
    apiResponse,
    NamingConvention.SnakeCase,
    NamingConvention.PascalCase);

The convention pass skips a member the mapper binds, or the configuration ignores or resolves with MapFrom. Any other member is filled while it still holds the value a new UserDto starts with, so what an AfterMap set is kept. A member initialised to a new instance, such as Tags = new(), is always filled, because every destination starts with a different instance.

Check Name Matching

// Check if names match across conventions
bool match = NamingConvention.NamesMatch(
    "user_name", NamingConvention.SnakeCase,
    "UserName", NamingConvention.PascalCase);
// Result: true

Matching compares the letters and digits and ignores case and where the words break, so user_id matches UserID and address_line_1 matches AddressLine1. When a name is converted, an acronym stays one word: HTTPServerID becomes http_server_id.

Real-World Example: External API Integration

public class ExternalApiClient
{
    private readonly HttpClient _http;

    public async Task<UserDto> GetUserAsync(int id)
    {
        // External API returns snake_case JSON
        var response = await _http.GetFromJsonAsync<ExternalUserResponse>($"/users/{id}");

        // Convert to C# conventions
        return response.MapWithConvention<ExternalUserResponse, UserDto>(
            NamingConvention.SnakeCase,
            NamingConvention.PascalCase);
    }
}

// External API response (snake_case)
public class ExternalUserResponse
{
    public int user_id { get; set; }
    public string first_name { get; set; }
    public string last_name { get; set; }
    public string email_address { get; set; }
    public DateTime created_at { get; set; }
}

// Internal DTO (PascalCase)
public class UserDto
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public DateTime CreatedAt { get; set; }
}

Clone this wiki locally