web-dev-qa-db-ja.com

JSON逆シリアル化-文字列は自動的にIntに変換されます

Newtonsoftを明示的に使用するか、ASP.NET Web Apiのモデルバインディングメカニズムを介して、JSONを以下のC#オブジェクトに非正規化すると、文字列id値が自動的にintに変換されます。タイプの不一致があるため、例外をスローしたり、エラーを発生させたりすることが予想されます。これは、JSONが仕様で機能することになっている方法ですか?そうでない場合、どうすればそのような自動変換を防ぐことができますか?

JSON:{"id":"4", "name":"a"} C#モデル:int id; string name

5
John L.

これはJson.NETの機能です。プリミティブ型を逆シリアル化すると、可能な場合は常にプリミティブJSON値がターゲットのc#型に変換されます。文字列"4"は整数に変換できるため、逆シリアル化は成功します。この機能が必要ない場合は、読み取られるトークンが本当に数値であることを確認する整数型の custom JsonConverter を作成できます(またはnull、 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);

最後に、JSONシリアライザー設定をWeb APIでグローバルに適用するには、たとえば ここ を参照してください。

15
dbc

最終的に必要な場合は、C#モデルを文字列にしてみてください。

0
Brendan Long

ほとんどの人がこの種の振る舞いを望んでいるので、あなたが説明するのは機能です。確認していませんが、文字列からターゲットのプロパティタイプに自動的に変換しようとするConvert.ChangeType(strValue, propertyType);のようなものを使用しているに違いありません。

文字列と同じように必要な場合は、Maksimのソリューションを使用してください。

モデルには、必要に応じて、両方のタイプを持つ追加のプロパティを組み込むこともできます。

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

    public string name { get; set; }
}
0
Alexei