System.Text.Json:使用自动强制转换反序列化JSON

时间:2019-11-29 01:31:18

标签: c# asp.net-core .net-core json.net system.text.json

使用.Net Core 3的新System.Text.Json JsonSerializer,如何自动转换类型(例如,将int转换为字符串并将string转换为int)?例如,这会引发异常,因为JSON中的id是数字,而C#的Product.Id需要一个字符串:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        var json = @"{""id"":1,""name"":""Foo""}";
        var o = JsonSerializer.Deserialize<Product>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
        });

        return View();
    }
}

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
}

Newtonsoft的Json.Net处理得很好。如果在C#期望一个字符串的情况下传递数字值(反之亦然),则没关系,一切都按预期进行了反序列化。如果您无法控制作为JSON传递的类型格式,如何使用System.Text.Json来处理呢?

5 个答案:

答案 0 :(得分:1)

您可以在模型类中使用 JsonNumberHandlingAttribute 来指定如何处理数字反序列化。允许的选项在 JsonNumberHandling 枚举中指定。

用法示例:

public class Product
{
    [JsonNumberHandling(JsonNumberHandling.WriteAsString)]
    public string Id { get; set; }
    
    public string Name { get; set; }
}

如果需要从stringint的序列化,可以使用JsonNumberHandling.AllowReadingFromString

答案 1 :(得分:1)

在选项中,将 NumberHandling 属性设置为 group color freq 0 A red 0.4 1 A red 0.4 2 A green 0.4 3 A blue 0.2 4 A green 0.4 5 B red 0.75 6 B red 0.75 7 B red 0.75 8 B green 0.25 9 C blue 0.33 10 C green 0.33 11 C red 0.33

AllowReadingFromString

答案 2 :(得分:0)

不用担心。只需在类中添加一个属性,该属性将以所需的类型返回所需的项目。

public class Product
{
    public int Id { get; set; }

    public string IdString 
    {
        get
        {
            return Id.ToString();
        }
    }

    public string Name { get; set; }
}

答案 3 :(得分:0)

  1. 新的System.Text.Json API公开了一个JsonConverter API,使我们可以根据需要转换类型。

    例如,我们可以创建一个从numberstring的通用转换器:

    public class AutoNumberToStringConverter : JsonConverter<object>
    {
        public override bool CanConvert(Type typeToConvert)
        {
            return typeof(string) == typeToConvert;
        }
        public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if(reader.TokenType == JsonTokenType.Number) {
                return reader.TryGetInt64(out long l) ?
                    l.ToString():
                    reader.GetDouble().ToString();
            }
            if(reader.TokenType == JsonTokenType.String) {
                return reader.GetString();
            }
            using(JsonDocument document = JsonDocument.ParseValue(ref reader)){
                return document.RootElement.Clone().ToString();
            }
        }
    
        public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
        {
            writer.WriteStringValue( value.ToString());
        }
    }
    
  2. 使用MVC / Razor Page时,我们可以在启动时注册此转换器:

    services.AddControllersWithViews().AddJsonOptions(opts => {
        opts.JsonSerializerOptions.PropertyNameCaseInsensitive= true;
        opts.JsonSerializerOptions.Converters.Insert(0, new AutoNumberToStringConverter());
    });
    

    ,然后MVC / Razor将自动处理类型转换。

  3. 或者如果您想手动控制序列化/反序列化:

    var opts = new JsonSerializerOptions {
        PropertyNameCaseInsensitive = true,
    };
    opts.Converters.Add(new AutoNumberToStringConverter());
    var o = JsonSerializer.Deserialize<Product>(json,opts) ;
    
  4. 您可以通过类似的方式启用字符串到数字类型的转换,如下所示:

    public class AutoStringToNumberConverter : JsonConverter<object>
    {
        public override bool CanConvert(Type typeToConvert)
        {
            // see https://stackoverflow.com/questions/1749966/c-sharp-how-to-determine-whether-a-type-is-a-number
            switch (Type.GetTypeCode(typeToConvert))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.Decimal:
                case TypeCode.Double:
                case TypeCode.Single:
                return true;
                default:
                return false;
            }
        }
        public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if(reader.TokenType == JsonTokenType.String) {
                var s = reader.GetString() ;
                return int.TryParse(s,out var i) ? 
                    i :
                    (double.TryParse(s, out var d) ?
                        d :
                        throw new Exception($"unable to parse {s} to number")
                    );
            }
            if(reader.TokenType == JsonTokenType.Number) {
                return reader.TryGetInt64(out long l) ?
                    l:
                    reader.GetDouble();
            }
            using(JsonDocument document = JsonDocument.ParseValue(ref reader)){
                throw new Exception($"unable to parse {document.RootElement.ToString()} to number");
            }
        }
    
    
        public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
        {
            var str = value.ToString();             // I don't want to write int/decimal/double/...  for each case, so I just convert it to string . You might want to replace it with strong type version.
            if(int.TryParse(str, out var i)){
                writer.WriteNumberValue(i);
            }
            else if(double.TryParse(str, out var d)){
                writer.WriteNumberValue(d);
            }
            else{
                throw new Exception($"unable to parse {str} to number");
            }
        }
    }
    

答案 4 :(得分:0)

对我来说不幸的是,itminus的示例不起作用,这是我的变体。

    /*
 * setPrice test
 */
@Test
public void testSetPrice_1() throws RecipeException {
    r1.setPrice("25");
    r1.setPrice("0");
}

/*
 * setPrice test
 */
@Test(expected = RecipeException.class)
public void testSetPrice_2() throws RecipeException {
    r1.setPrice("adsada");
    r1.setPrice(" ");
    r1.setPrice("-1");
}