JSON反序列化——String自动转换为Int

问题描述 投票:0回答:3

当我将 JSON 反序列化为下面的 C# 对象时,无论是显式使用 Newtonsoft 还是通过 ASP.NET Web Api 的模型绑定机制,字符串

id
值都会自动转换为 int。我希望它会抛出异常或引发错误,因为存在类型不匹配。这是 JSON 在规范中应该如何工作的吗?如果没有,我怎样才能阻止这种自动转换?

JSON:

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

c# json asp.net-mvc asp.net-web-api json.net
3个回答
32
投票

这是 Json.NET 的一个功能:当反序列化原始类型时,它会尽可能将原始 JSON 值转换为目标 C# 类型。 由于字符串

"4"
可以转换为整数,因此反序列化成功。 如果您不想要此功能,您可以为整数类型创建一个 custom
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;
    }        
}

请注意,转换器接受像

4.00
这样的整数值。 如果不满足您的需求,您可以通过删除
JsonToken.Float
的检查来更改此设置。

您可以将其直接应用到您的模型中,如下所示:

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

    public string name { get; set; }
}

或者将转换器包含在

JsonSerializerSettings
中以将其应用于所有积分字段:

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

最后,要在 Web API 中全局应用 JSON 序列化器设置,请参见示例此处


0
投票

您所描述的是一个功能,因为大多数人都想要这种行为。我没有检查过,但我敢打赌它使用类似

Convert.ChangeType(strValue, propertyType);
的东西来尝试自动从字符串转换为目标的属性类型。

如果您只需要它作为字符串,请使用 Maksim 的解决方案。

如果需要,您的模型还可以包含额外的属性以具有这两种类型:

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

    public string name { get; set; }
}

0
投票

这对我解决类似问题很有帮助。调用 deserialize 时,您可以使用 JsonSerializerOptions 指定禁止从 int 转换为 string,例如

var options = new JsonSerializerOptions();
options.NumberHandling = 0; //disallow reading from string     
_serializerOptions = options; 
var payload = JsonSerializer.Deserialize<IPayload>(body,_serializerOptions);
© www.soinside.com 2019 - 2024. All rights reserved.