Как изменить тип по умолчанию для числовой десериализации? - программирование
Подтвердить что ты не робот

Как изменить тип по умолчанию для числовой десериализации?

Я десериализую некоторые свойства для Dictionary<string, object>.

Когда я десериализую некоторый json, он заполняет Dictionary объектами Int64, а не Int32. Я бы хотел, чтобы он выбрал Int32 как стандарт по умолчанию, зная, что у меня может быть javascript Numerics, который будет переполняться при преобразовании. Выброс исключения в этом случае был бы вполне приемлемым.

Есть ли способ достичь этого? Я надеюсь на некоторые приятные атрибуты или удобный интерфейс, который может быть реализован и добавлен в JsonSerializer. И я боюсь, что мне нужно углубиться в глубины Json.NET.

В принципе, я хотел бы иметь некоторый способ управления известными типами объектов, чтобы я мог получить Int32 вместо Int64 и DateTimes вместо Strings.

4b9b3361

Ответ 1

Насколько я знаю, нет встроенного способа сделать это.

Была проблема на эту тему, но она была закрыта. Некоторые комментарии автора по этому вопросу:

Json.NET по умолчанию считывает целочисленные значения как Int64, потому что нет способа узнать, должно ли значение быть Int32 или Int64, а Int64 с меньшей вероятностью переполнится. Для типизированного свойства десериализатор знает, как преобразовать Int64 в Int32, но поскольку ваше свойство не типизировано, вы получаете Int64. [...] Так работает Json.NET.

Самым простым решением было бы изменить тип Dictionary<string, int> на Dictionary<string, int>, но я полагаю, что вы не только читаете цифры и, следовательно, зависаете с object.

Другой вариант - использовать обратные вызовы сериализации и вручную конвертировать эти Int64 в Int32 или создать свой собственный. Контрактный резолвер JsonConverter и напрямую контролируйте (de-) сериализацию.


Изменить: я создал небольшой пример, чтобы быть более конкретным.

Вот очень простой конвертер, который работает только с вашим конкретным словарем:

public class Int32Converter : JsonConverter {
    public override bool CanConvert(Type objectType) {
        // may want to be less concrete here
        return objectType == typeof(Dictionary<string, object>);
    }

    public override bool CanWrite {
        // we only want to read (de-serialize)
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        // again, very concrete
        Dictionary<string, object> result = new Dictionary<string, object>();
        reader.Read();

        while (reader.TokenType == JsonToken.PropertyName) {
            string propertyName = reader.Value as string;
            reader.Read();

            object value;
            if (reader.TokenType == JsonToken.Integer)
                value = Convert.ToInt32(reader.Value);      // convert to Int32 instead of Int64
            else
                value = serializer.Deserialize(reader);     // let the serializer handle all other cases
            result.Add(propertyName, value);
            reader.Read();
        }

        return result;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        // since CanWrite returns false, we don't need to implement this
        throw new NotImplementedException();
    }
}

Вы можете использовать атрибуты для украшения элементов вашим конвертером или передать его в качестве параметра в метод сериализации (de-). Вот пример, где я использовал атрибут:

    [JsonObject]
public class MyObject {
    [JsonConverter(typeof(Int32Converter))]
    public Dictionary<string, object> Properties { get; set; }
}

И вот код, который я использовал для проверки реализации:

class Program {
    static void Main(string[] args) {
        MyObject test = new MyObject();
        test.Properties = new Dictionary<string, object>() { { "int", 15 }, { "string", "hi" }, { "number", 7 } };
        Print("Original:", test);

        string json = JsonConvert.SerializeObject(test);
        Console.WriteLine("JSON:\n{0}\n", json);

        MyObject parsed = JsonConvert.DeserializeObject<MyObject>(json);
        Print("Deserialized:", parsed);
    }

    private static void Print(string heading, MyObject obj) {
        Console.WriteLine(heading);
        foreach (var kvp in obj.Properties)
            Console.WriteLine("{0} = {1} of {2}", kvp.Key, kvp.Value, kvp.Value.GetType().Name);
        Console.WriteLine();
    }
}

Без конвертера результат будет:

Deserialized:
int = 15 of Int64
string = hi of String
number = 7 of Int64

и с конвертером это:

Deserialized:
int = 15 of Int32
string = hi of String
number = 7 of Int32

Ответ 2

Попробуйте

   var variable = Convert.ToInt32(object) 

Повторяйте Dictionary<string,object> один раз и перепишите его object с помощью этого Int32 или выполните преобразование Int32 каждый раз, когда вы читаете object.

Ответ 3

Это хорошо работает для меня:

public class ParseNumbersAsInt32Converter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(long) || objectType == typeof(long?) || objectType == typeof(object);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value != null && reader.Value is long)
        {
            return Convert.ToInt32(reader.Value);
        }
        return reader.Value;
    }
}

Ответ 4

Я принимаю ответ Enzi, потому что это то, о чем я просил.

Однако с тех пор я изменил свою стратегию.

Сейчас я десериализую , который вместо словаря имеет строго типизированный объект Entity (T) с изменениями. Он также имеет List<string> с именами свойств свойств, которые присутствовали в входящем json. Затем я заполняю этот список во время десериализации, используя специальный MediaFormatter. Таким образом, я получаю строго типизированный объект и исправляю десериализацию всех свойств, и из списка знаю, какие свойства я должен установить в своей коллекции T, когда я хочу выполнить свою пакетную операцию.

Таким образом, я в основном использую свои сущности как DTO, не имея множество разных DTO для разных пакетных операций. Является довольно гладким, если я так говорю.:)