Open timdoke opened 6 years ago
Yes, i agree it would be good to see something like the Jil:
[JilDirective(TreatEnumerationAs = typeof(int))]
That can be done while setting up the options.. I meant to handle both like newtonsoft does.
Currently EnumFormatter accepts both.
public enum Fruit
{
Orange = 0,
Apple = 1
}
var apple1 = JsonSerializer.Deserialize<Fruit>("\"Apple\"");
var apple2 = JsonSerializer.Deserialize<Fruit>("1");
Console.WriteLine(apple1);
Console.WriteLine(apple2);
// Serialize Option
var v1 = JsonSerializer.Serialize(Fruit.Apple, EnumResolver.Default); // serialize as string
var v2 = JsonSerializer.Serialize(Fruit.Apple, EnumResolver.UnderlyingValue); // serialize as value
Console.WriteLine(Encoding.UTF8.GetString(v1)); // "Apple"
Console.WriteLine(Encoding.UTF8.GetString(v2)); // 1
Hmm.. I have these lines of code set up inside my startup under services.AddMvcCore..
options.InputFormatters.RemoveType
options.InputFormatters.Add(new JsonInputFormatter()); options.OutputFormatters.Add(new JsonOutputFormatter());
Those formatters are from utf8Json.
In the swagger configuration, I have DescribeAllEnumsAsStrings set with SwaggerGenOptions.
The problem is when I send stuff to the API end point, it thinks the object is null if I serialize it as a value.. If I serialize it as a string, it is fine.. Am I doing something incorrectly on setting up the formatters?
Please show concrete types and concrete code. Also, a code that can explain the problem more concisely.
Sure.. The problem is around the way that REST clients are generated with autorest or with the built in vs generator from swagger (if not using .net core). If, from swagger, you choose to serialize enums to string, the FruitPickerGenerated type class is made. I guess the issue (or feature?) is that utf8json is more strongly typed and isn't flexible enough to automatically convert an int to a string (in this case).
namespace Utf8JsonEnumTesting
{
class Program
{
static void Main(string[] args)
{
TestCombo();
Console.WriteLine("done!");
Console.Read();
}
private static void TestCombo()
{
Console.WriteLine("Starting TestCombo");
FruitPicker fp = new FruitPicker();
fp.Name = "Greg";
fp.Fruit = Fruit.Apple;
const string incomingStringFromMicroservice = @"{""Name"":""Greg"",""Fruit"":1}";
//translates 1 to "1". Then, when we do a convert to the actual enum value in the project with automapper, and gives a correct object
var newtonsoftFruit = Newtonsoft.Json.JsonConvert.DeserializeObject<FruitPickerGenerated>(incomingStringFromMicroservice);
//errors as it is expecting a string instead of 1
var utf8Fruit = Utf8Json.JsonSerializer.Deserialize<FruitPickerGenerated>(incomingStringFromMicroservice);
}
}
public class FruitPicker
{
public string Name { get; set; }
public Fruit Fruit { get; set; }
}
public enum Fruit
{
Orange = 0,
Apple = 1
}
public class FruitPickerGenerated
{
public string Name { get; set; }
public string Fruit { get; set; }
}
}
Thanks, sample code is nice.
Utf8Json supports int -> enum and string -> enum.
You can deserialize
var utf8Fruit = Utf8Json.JsonSerializer.Deserialize<FruitPicker>(incomingStringFromMicroservice);
But your example shows int -> string
convert.
This convert is not supported on default.
You can create custom converter.
public class DurableStringFormatter : IJsonFormatter<string>
{
public void Serialize(ref JsonWriter writer, string value, IJsonFormatterResolver formatterResolver)
{
writer.WriteString(value);
}
public string Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
var token = reader.GetCurrentJsonToken();
switch (token)
{
case JsonToken.Number:
return reader.ReadDouble().ToString();
case JsonToken.String:
case JsonToken.Null:
return reader.ReadString();
case JsonToken.True:
case JsonToken.False:
return reader.ReadBoolean().ToString();
default:
throw new JsonParsingException("Current token is invalid. Token:" + token);
}
}
}
and adopt it.
public class FruitPickerGenerated
{
public string Name { get; set; }
[JsonFormatter(typeof(DurableStringFormatter))]
public string Fruit { get; set; }
}
Currently can not replace formatter on global if target type is primitive. I will try to configure it in a while.
Thanks for the reply. Unfortunately, the class and properties are auto generated and partial properties are not supported. So, once we regenerate the source, it will be lost. So, that isn't really a good solution.. I have a work around by making sure the outgoing httpclient formats the json in the correct fashion... But, it would be nice if I didn't have to do that work around. Thanks!
Is it possible with the utf8json JsonInputFormatter, to have it accept both enums as ints and enums as strings? I don't see any options on the JsonInputFormatter class. On swagger, I'm converting enums to strings.. But, in microservice communication, I want it to be able to use enums as ints..