Humanize and parse enums and flags
Orientation
Enum humanization resolves one display label for each member: supported description/display metadata when present, otherwise the humanized member name. On Humanizer 4, dehumanization also recognizes the raw name, its humanized form, and authored display aliases. Flags enums are decomposed and joined through the active collection formatter.
Example
The executable verifies a two-flag value, explicit enum-name and
DisplayAttribute source selection, every dehumanization alias,
case-insensitive generic dehumanization, no-match behavior, metadata fallback,
and an unknown flag bit:
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Humanizer;
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
var access = Access.Read | Access.Write;
AssertEqual("Can view and Can edit", access.Humanize());
AssertEqual(
"Read and Write",
access.Humanize(LetterCasing.Title, EnumHumanizeSource.EnumName));
AssertEqual(Access.Read, "CAN VIEW".DehumanizeTo<Access>());
AssertEqual(null, "missing".DehumanizeTo<Access>(OnNoMatch.ReturnsNull));
AssertEqual("", ((Access)8).Humanize());
AssertEqual("Awaiting reviewer", DeliveryState.NeedsReview.Humanize());
AssertEqual(
"Awaiting reviewer",
DeliveryState.NeedsReview.Humanize(
LetterCasing.AllCaps,
EnumHumanizeSource.Default));
AssertEqual(
"Needs review",
DeliveryState.NeedsReview.Humanize(
LetterCasing.Sentence,
EnumHumanizeSource.EnumName));
AssertEqual(
"Review required",
DeliveryState.NeedsReview.Humanize(
LetterCasing.AllCaps,
EnumHumanizeSource.DisplayName));
AssertEqual(
"Awaiting reviewer",
DeliveryState.NeedsReview.Humanize(
LetterCasing.AllCaps,
EnumHumanizeSource.DisplayDescription));
AssertEqual(
"Review",
DeliveryState.NeedsReview.Humanize(
LetterCasing.AllCaps,
EnumHumanizeSource.DisplayShortName));
AssertEqual(
"Waiting in queue",
DeliveryState.QueuedItem.Humanize(
LetterCasing.AllCaps,
EnumHumanizeSource.DisplayShortName));
AssertEqual(
"Read",
Access.Read.Humanize(
LetterCasing.Sentence,
EnumHumanizeSource.DisplayName));
AssertEqual(DeliveryState.NeedsReview, "NeedsReview".DehumanizeTo<DeliveryState>());
AssertEqual(DeliveryState.NeedsReview, "Needs review".DehumanizeTo<DeliveryState>());
AssertEqual(DeliveryState.NeedsReview, "Review required".DehumanizeTo<DeliveryState>());
AssertEqual(DeliveryState.NeedsReview, "Awaiting reviewer".DehumanizeTo<DeliveryState>());
AssertEqual(DeliveryState.NeedsReview, "REVIEW".DehumanizeTo<DeliveryState>());
Console.WriteLine("Can view and Can edit; explicit sources; all aliases -> NeedsReview");
static void AssertEqual<T>(T expected, T actual)
{
if (!EqualityComparer<T>.Default.Equals(expected, actual))
throw new InvalidOperationException($"Expected '{expected}', got '{actual}'.");
}
[Flags]
enum Access
{
[Display(Description = "None")]
None = 0,
[Display(Description = "Can view")]
Read = 1,
[Display(Description = "Can edit")]
Write = 2
}
enum DeliveryState
{
[Display(
Name = "Review required",
Description = "Awaiting reviewer",
ShortName = "Review")]
NeedsReview,
[Display(Name = "Waiting in queue")]
QueuedItem
}
Choose metadata and casing
Humanizer prefers supported description/display metadata before deriving words
from the member name. That chosen value is also the member's dehumanization
label. On Humanizer 4, LetterCasing changes only labels derived from the
member name; authored metadata preserves its exact casing.
Configurator.UseEnumDescriptionPropertyLocator can select a custom
description-like property globally and must run before enum metadata is
cached.
Select the humanization source
On Humanizer 4, pass both LetterCasing and EnumHumanizeSource when the
default metadata precedence is not the desired presentation:
Defaultpreserves the existing precedence. ADisplayAttributeuses itsDescription, then itsName, then the member name. Without that attribute, a configured description-like property is checked before the member name.EnumNamederives text from the member name and applies the requested casing.DisplayNameusesDisplayAttribute.Name.DisplayDescriptionusesDisplayAttribute.Description.DisplayShortNameusesDisplayAttribute.ShortName, then itsName.
If selected display metadata is missing, Humanizer falls back to the humanized
enum member name and applies the requested casing. Authored display metadata
preserves its casing, and localized DisplayAttribute getters are honored. For
flags, the selected source and fallback apply independently to each constituent
before the labels are joined.
Source selection intentionally uses the
Humanize(LetterCasing, EnumHumanizeSource) overload; there is no source-only
overload, so existing calls such as Humanize(default) and Humanize(0) keep
their prior meaning. An undefined EnumHumanizeSource throws
ArgumentOutOfRangeException. Explicit source selection reads only the chosen
DisplayAttribute property, so an unrelated invalid localized property does
not break the call. A custom description-property locator affects the default
path, not the explicitly selected DisplayAttribute sources. It does not
change dehumanization aliases or their collision rules.
Parse accepted aliases
On Humanizer 4, DehumanizeTo<TEnum> recognizes the raw member name, its
humanized form, and DisplayAttribute name, description, and short-name
values. It also recognizes a configured description attribute value when that
value becomes the member's authored description. Matching is
case-insensitive, including for flags, but does not trim whitespace.
Handle missing input
The default DehumanizeTo<TEnum> throws NoMatchFoundException. Pass OnNoMatch.ReturnsNull to the nullable generic overload when an unknown label is expected.
Format flags
Known nonzero flags are humanized and joined using the current UI culture. A defined zero member uses its label. A value containing only unknown bits humanizes to an empty string.
Pitfall
Aliases should be unambiguous even though matching is case-insensitive. On
Humanizer 4, a current humanized representation takes precedence over a
supplemental alias when labels collide; collisions among supplemental aliases
retain the later enum value in unsigned numeric order. Prefer the generic enum
overloads. Runtime enum humanization and the runtime Type dehumanization
overload use reflection and are annotated with RequiresDynamicCode and
RequiresUnreferencedCode; they are not the safe default for trimmed or Native
AOT applications.
Version notes
Enum APIs span the documented corpus, but Humanizer 3 changes generic
constraints and metadata configuration. The flags example runs against the
selected package. Consult the selected API before migrating code that stores
values as the base Enum type. Metadata casing preservation when a
LetterCasing argument is supplied, and dehumanization through raw,
humanized, and supplemental metadata aliases, are Humanizer 4 behavior.
EnumHumanizeSource and its two casing-plus-source overloads are also
Humanizer 4 APIs.