简体   繁体   English

JSON 反序列化 - 字符串自动转换为 Int

[英]JSON Deserialization - String Is Automatically Converted To Int

When I deseiralize the JSON to the C# object below, either using Newtonsoft explicitly or via the model binding mechanism of ASP.NET Web Api, the string id value is automatically converted to int.当我将 JSON 反序列化为下面的 C# 对象时,无论是显式使用 Newtonsoft 还是通过 ASP.NET Web Api 的模型绑定机制,字符串id值都会自动转换为 int。 I would expect it to throw an exception or raise an error as there is a type mismatch.我希望它抛出异常或引发错误,因为存在类型不匹配。 Is this how JSON is supposed to work in the specs?这是 JSON 在规范中应该如何工作吗? If not, how can I prevent such an automatic conversion?如果没有,我怎样才能防止这种自动转换?

JSON: {"id":"4", "name":"a"} C# model: int id; string name JSON: {"id":"4", "name":"a"} C# 模型: int id; string name int id; string name

This is a feature of Json.NET: when deserializing a primitive type, it will convert the primitive JSON value to the target c# type whenever possible. 这是Json.NET的一个特性:在反序列化基本类型时,它会尽可能地将原始JSON值转换为目标c#类型。 Since the string "4" can be converted to an integer, deserialization succeeds. 由于字符串"4"可以转换为整数,因此反序列化成功。 If you don't want this feature, you can create a custom JsonConverter for integral types that checks that the token being read is really numeric (or null , for a nullable value): 如果你不想要这个功能,你可以创建一个自定义的JsonConverter为整数类型,检查所读的令牌是真正的数字(或null ,为空值):

public class StrictIntConverter : JsonConverter
{
    readonly JsonSerializer defaultSerializer = new JsonSerializer();

    public override bool CanConvert(Type objectType) 
    {
        return objectType.IsIntegerType();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        switch (reader.TokenType)
        {
            case JsonToken.Integer:
            case JsonToken.Float: // Accepts numbers like 4.00
            case JsonToken.Null:
                return defaultSerializer.Deserialize(reader, objectType);
            default:
                throw new JsonSerializationException(string.Format("Token \"{0}\" of type {1} was not a JSON integer", reader.Value, reader.TokenType));
        }
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

public static class JsonExtensions
{
    public static bool IsIntegerType(this Type type)
    {
        type = Nullable.GetUnderlyingType(type) ?? type;
        if (type == typeof(long)
            || type == typeof(ulong)
            || type == typeof(int)
            || type == typeof(uint)
            || type == typeof(short)
            || type == typeof(ushort)
            || type == typeof(byte)
            || type == typeof(sbyte)
            || type == typeof(System.Numerics.BigInteger))
            return true;
        return false;
    }        
}

Note the converter accepts values like 4.00 as integers. 请注意,转换器接受4.00值作为整数。 You can change this by removing the check for JsonToken.Float if it does not meet your needs. 如果JsonToken.Float不符合您的需要,可以通过删除它来更改此项。

You can apply it to your model directly as follows: 您可以直接将其应用于您的模型,如下所示:

public class RootObject
{
    [JsonConverter(typeof(StrictIntConverter))]
    public int id { get; set; }

    public string name { get; set; }
}

Or include the converter in JsonSerializerSettings to apply it to all integral fields: 或者将转换器包含在JsonSerializerSettings以将其应用于所有整数字段:

var settings = new JsonSerializerSettings
{
    Converters = { new StrictIntConverter() },
};
var root = JsonConvert.DeserializeObject<RootObject>(json, settings);

Finally, to apply JSON serializer settings globally in Web API, see for instance here . 最后,要在Web API中全局应用JSON序列化程序设置,请参阅此处

尝试将C#模型设为字符串,如果这是您最终想要的。

What you describe is a feature, since most folks want this kind of behavior. 你描述的是一个特征,因为大多数人都想要这种行为。 I haven't check, but I bet it uses something like Convert.ChangeType(strValue, propertyType); 我没有检查,但我敢打赌它使用像Convert.ChangeType(strValue, propertyType); that tries to automatically convert from string to target's property type. 尝试自动从字符串转换为目标的属性类型。

If you need it just as string, use Maksim's solution. 如果您需要它作为字符串,请使用Maksim的解决方案。

Your model can also incorporate an extra property to have both types, if needed: 如果需要,您的模型还可以包含额外的属性以包含两种类型:

public class Model
{
    public int id { get; set; }
    public string idStr => id.ToString();

    public string name { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM