NSwag Studio: DateTime and Enums
Hello,
First of all, thanks for your contribution by creating this awesome project. I am using NSwagStudio to generate C# Client class from a swagger spec, and I'm having issues with DateTime and Enum in ConvertToString method.
Here is the NSwagStudio generated code for the ConvertToString method (all request params are converted to string using this method):
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value is System.Enum)
{
string name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value;
}
}
}
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[]) value);
}
else if (value != null && value.GetType().IsArray)
{
var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
}
return System.Convert.ToString(value, cultureInfo);
}
-
For DateTime conversion, I am getting the string format of the current culture, in my case spanish, and that's causing an error on the API side because it isn't able to parse the value back to DateTime. It would be great if we can pass anywhere the DateTime format to convert to, before sending to the API.
-
For Enum conversion, I am always getting the ToString() value of the Enum name:
Swagger Spec fragment for Enum class:
{
"name": "TransferType",
"in": "formData",
"required": true,
"type": "integer",
"format": "int32",
"enum": [
0,
1
]
},
Original Enum class:
public enum TransferCombination
{
OneWay, RoundTrip
}
NSwagStudio Generated Enum class:
public enum TransferType
{
_0 = 0,
_1 = 1,
}
Here I would like to, first of all be able to get original names in the NSwagStudio Generated Enum class, and also be able to decide if send enum value or enum name to the API in the request.
Thank you Best regards David
Enums: https://github.com/RSuter/NJsonSchema/wiki/Enums
OK Enums are solved with that, thank you. Any news on the DateTime issue?
I see on NSwagStudio there is a field that is for configuring something related to DateTime format, see: http://prntscr.com/l7vbph
But it has no effect if I put a custom format like 'yyyy-MM-dd HH:mm:ss' for example.
Thank you Best David
I think this setting is only for datetime operation parameters... but i think in your case it's a datetime property and thus directly handled by the json serializer... I think there is an option to expose the serializer settings creation method and so that you can customize these settings with other datetime serialization behavior....
I think the ConvertToString method already returns the datetime converted to string, so the serializer, which is executed later on, won't be able to change its format. I think you could add a DateTime format property on the C# Client class and expose it so users can change it. Then in the ConvertToString method, add another case to handle DateTime conversion:
else if (value != null && value is System.DateTime)
{
return ((System.DateTime)value).ToString(_CustomFormat);
}
where _CustomFormat would be a property of the class that can be changed when instantiating the ClientClass or later on through a setter.
What do you think about that?
Best David
I think you could add a DateTime format property on the C# Client class and expose it so users can change it.
I think that's a bad idea because the serialization logic which matches the backend should be hidden from the user of the class... i.e. the user should not be able to set client settings which do not match the backend.
I think _CustomFormat should be a setting of the C# client generator...
Yes, that would be valid for one general date format, but in case you may want different formats for different DateTime fields on the same API, you'd end up on the same problem.
One possible option would be to create DateTime fields as string types, that way users would pass directly the format the want to receive on the API side. Another better option: you can have a default DateTime format (which you can allow the user to define on the Client Generator (NSwagStudio in my case). Then you can create an overload for each API method containing DateTime fields, in which that fields would have type 'string'. So the user can decide to use the overload with DateTime fields and let the class to use the default DateTime format defined when creating it; or it can use the overload with string fields and pass it custom format to the API call.
@RicoSuter, would it be possible to use the same format specified in the options?
Since NswagStudio has already defined the date format to use among its options, the code of the ConvertToString method should be modified to use the chosen format. Just adding 3 lines of code (see below)
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
{
return "";
}
if (value is System.Enum)
{
var name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value != null ? attribute.Value : name;
}
}
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[]) value);
}
else if (value is string[])
{
return string.Join(",", (string[])value);
}
else if (value.GetType().IsArray)
{
var valueArray = (System.Array)value;
var valueTextArray = new string[valueArray.Length];
for (var i = 0; i < valueArray.Length; i++)
{
valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo);
}
return string.Join(",", valueTextArray);
}
else if (value is DateTime || value is DateTimeOffset || value is DateTime? || value is DateTimeOffset?)
{
return ((IFormattable)value).ToString("s", cultureInfo);
}
var result = System.Convert.ToString(value, cultureInfo);
return result == null ? "" : result;
}
I mean I carved the "s" here, but in reality this parameter should be exactly what is indicated in the UI. (see screenshot)
return ((IFormattable)value).ToString("s", cultureInfo);