Reputation: 116928
I have an enum as follows. I also have a string in-progress
I am trying to parse this string to the appropriate enum. As seen by the following test we want to parse to take a string and return the enum
[Fact]
public void TestParseOfEnum()
{
var data = "not-started";
var parsed = EnumExtensions.GetValueFromEnumMember<CarePlan.CarePlanActivityStatus>(data);
parsed.ShouldBe(CarePlan.CarePlanActivityStatus.NotStarted);
}
The issue being that enum try parse is checking on the name which means its failing. I need to parse it on this custom attribute.
CarePlan.CarePlanActivityStatus status
Enum.TryParse("in-progress", out status)
A try parse will not find this as try parse checks on the name field and not on the custom attribute within this enum. So this would return false and not find my enum
I have been trying to see how I could get a list back of all of the fields within the enum and then test on the literal.
This would work but i have to specify each of the values within the enum in getfield
var res = typeof(CarePlan.CarePlanActivityStatus)
.GetField(nameof(CarePlan.CarePlanActivityStatus.Cancelled))
.GetCustomAttribute<EnumLiteralAttribute>(false)
.Literal;
I tried something like this but Literal doesn't exist at this point apparently so this fails as well.
var hold = typeof(CarePlan.CarePlanActivityStatus).GetFields().Where(a =>
a.GetCustomAttributes<EnumLiteralAttribute>(false).Literal.Equals(data));
The enum
The nuget package i am using for the fhir library is Hl7.Fhir.R4
[FhirEnumeration("CarePlanActivityStatus")]
public enum CarePlanActivityStatus
{
/// <summary>
/// Care plan activity is planned but no action has yet been taken.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("not-started", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Not Started")] NotStarted,
/// <summary>
/// Appointment or other booking has occurred but activity has not yet begun.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("scheduled", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Scheduled")] Scheduled,
/// <summary>
/// Care plan activity has been started but is not yet complete.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("in-progress", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("In Progress")] InProgress,
/// <summary>
/// Care plan activity was started but has temporarily ceased with an expectation of resumption at a future time.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("on-hold", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("On Hold")] OnHold,
/// <summary>
/// Care plan activity has been completed (more or less) as planned.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("completed", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Completed")] Completed,
/// <summary>
/// The planned care plan activity has been withdrawn.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("cancelled", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Cancelled")] Cancelled,
/// <summary>
/// The planned care plan activity has been ended prior to completion after the activity was started.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("stopped", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Stopped")] Stopped,
/// <summary>
/// The current state of the care plan activity is not known. Note: This concept is not to be used for "other" - one of the listed statuses is presumed to apply, but the authoring/source system does not know which one.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("unknown", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Unknown")] Unknown,
/// <summary>
/// Care plan activity was entered in error and voided.
/// (system: http://hl7.org/fhir/care-plan-activity-status)
/// </summary>
[EnumLiteral("entered-in-error", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Entered in Error")] EnteredInError,
}
// example of everything I have tested with
The nuget package i am using for the fhir library is Hl7.Fhir.R4
public static class EnumExtensions
{
public static T GetValueFromEnumMember<T>(string value) where T : Enum
{
{
// Doesnt work as .Literal shows as red not valid.
var hold = typeof(CarePlan.CarePlanActivityStatus).GetFields().Where(a =>
a.GetCustomAttributes<EnumLiteralAttribute>(false).Literal.Equals(data));
// doesnt work as its returning the string i need it to return the enum
var res = typeof(CarePlan.CarePlanActivityStatus)
.GetField(nameof(CarePlan.CarePlanActivityStatus.Cancelled))
.GetCustomAttribute<EnumLiteralAttribute>(false)
.Literal;
// neither of the following two work as they are just looping though and i cant find the enum they find.
foreach (CarePlan.CarePlanActivityStatus status in (CarePlan.CarePlanActivityStatus[])Enum.GetValues(
typeof(CarePlan.CarePlanActivityStatus)))
{
}
foreach (string i in Enum.GetNames(typeof(CarePlan.CarePlanActivityStatus)))
{
// var res = typeof(CarePlan.CarePlanActivityStatus)
// .GetField(nameof(CarePlan.CarePlanActivityStatus[i]))
// .GetCustomAttribute<EnumLiteralAttribute>(false)
// .Literal;
//
// Console.WriteLine($" {res}" );
//
// Console.WriteLine($" {i}" );
}
}
Isnt there a way to parse a string to an enum without making a large mess of a if statements to test it. Ideally i need to create a generic method as i have about 10 of these enums i need test.
Upvotes: 3
Views: 1701
Reputation: 42235
I tried something like this but Literal doesn't exist at this point apparently so this fails as well.
One error in your question is that you're calling GetCustomAttributes
rather than GetCustomAttribute
. GetCustomAttributes
returns an IEnumerable<EnumLiteralAttribute>
, which won't of course have a Literal
property. GetCustomAttribute
returns a single EnumLiteralAttribute
(or null
), and you can acces its Literal
property.
This would produce the error message:
error CS1061: 'IEnumerable<EnumLiteralAttribute>' does not contain a definition for 'Literal' and no accessible extension method 'Literal' accepting a first argument of type 'IEnumerable<EnumLiteralAttribute>' could be found (are you missing a using directive or an assembly reference?)
Another issue is that this will then fail with a NullReferenceException
. If you look at what GetFields()
is actually returning, the first field it returns is value__
, which doesn't have any attributes on it. So your GetCustomAttribute<EnumLiteralAttribute>()
returns null
, and accessing its Literal
member fails with a NullReferenceException
.
If you exclude this field from your test, or just make sure that you're safe to GetCustomAttribute
returning null
, everything works fine.
E.g.:
var hold = typeof(CarePlanActivityStatus).GetFields()
.Where(a => a.GetCustomAttribute<EnumLiteralAttribute>(false)?.Literal == data);
If you then want to access the enum which has that attribute, call the FieldInfo
's GetValue
method:
var hold = typeof(CarePlanActivityStatus).GetFields()
.FirstOrDefault(a => a.GetCustomAttribute<EnumLiteralAttribute>(false)?.Literal == data)
?.GetValue(null);
From the comments, you can have multiple attributes on a single field, and you want to see if any of their Literal
attributes matches data
.
In this case you will want to use GetCustomAttributes
to get all attributes, but you then want to loop through them and see if the Literal
attribute on any of them matches what you want. The easiest way to do this is with Linq's Any
:
string data = "cancelled";
var hold = typeof(CarePlanActivityStatus).GetFields()
.FirstOrDefault(a => a.GetCustomAttributes<EnumLiteralAttribute>(false)
.Any(x => x.Literal == data))
?.GetValue(null);
hold.Dump();
Upvotes: 0
Reputation: 1
you can use this:
var enumType = typeof(CarePlanActivityStatus);
FieldInfo field = enumType.GetField(nameof(CarePlanActivityStatus.Cancelled));
var enumLiteralAttribute = field.GetCustomAttribute<EnumLiteralAttribute>(false);
WriteLine(enumLiteralAttribute.Name); // canceled
WriteLine(enumLiteralAttribute.Url); // http://hl7.org/fhir/care-plan-activity-status
Upvotes: -1
Reputation: 1363
You can try with a extension method to read the Custom Atribute from Enums:
public static class EnumExtensions
{
public static T GetValueFromEnumMember<T>(string value) where T: Enum
{
var type = typeof(T);
foreach (var field in type.GetFields())
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(EnumMemberAttribute)) as EnumMemberAttribute;
if (attribute != null)
{
if (attribute.Value == value)
return (T)field.GetValue(null);
}
else
{
if (field.Name == value)
return (T)field.GetValue(null);
}
}
throw new ArgumentException($"unknow value: {value}");
}
}
and use it like this:
EnumExtensions.GetValueFromEnumMember<CarePlanActivityStatus>(stringValueToTest)
it returns the Enum Value
Upvotes: 3