MVC3 RTM терпит неудачу приведения типа числа при десериализации JSON - PullRequest
4 голосов
/ 18 февраля 2011

Просто поместите данные сериализации в формат "application / json; charset = utf-8" в MVC3 (и, возможно, в более старых версиях). То, что происходит, - это все числа со значениями NULL, заканчивающимися нулями, а числа типа «десятичные» заканчиваются на 0, когда сериализуют их внутри объекта javascript (в JSON) и оставляют их как числа, а не строки.

Вот пример кода, который иллюстрирует это неправильное поведение
- - - этот пример был создан с использованием jquery-1.4.4.js и jquery.json-2.2.js - - - -

HomeController.cs:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.SaveUrl = Url.Action("Save", "Home", new { inspectionFormID = Guid.Empty }, Request.Url.Scheme);
        return View();
    }

    public JsonResult Save(Guid inspectionFormID, JsonTest result)
    {
        return Json(result);
    }

    public class JsonTest 
    {
        public double Double { get; set; }
        public double? DoubleNull { get; set; }

        public decimal Decimal { get; set; }
        public decimal? DecimalNull { get; set; }

        public Double Double2 { get; set; }
        public Double? Double2Null { get; set; }

        public Decimal Decimal2 { get; set; }
        public Decimal? Decimal2Null { get; set; }

        public Single Single { get; set; }
        public Single? SingleNull { get; set; }

        public float Float { get; set; }
        public float? FloatNull { get; set; }

        public int Int { get; set; }
        public int? IntNull { get; set; }

        public Int64 Int64 { get; set; }
        public Int64? Int64Null { get; set; }
    }

}

Index.cshtml:

    @{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<b>@ViewBag.SaveUrl</b>
<br />
<hr />
<br />

<h3>Integral Numbers</h3>
<button type="button" class="a">Clicky</button>
<div></div>

<h3>Decimal Numbers (xx.0)</h3>
<button type="button" class="b">Clicky</button>
<div></div>

<h3>Decimal Numbers (xx.5)</h3>
<button type="button" class="c">Clicky</button>
<div></div>

<h3>Integral Numbers as strings</h3>
<button type="button" class="d">Clicky</button>
<div></div>

<h3>Decimal Numbers as strings (xx.5)</h3>
<button type="button" class="e">Clicky</button>
<div></div>

<script type="text/javascript">

    $(function () {
        var saveUrl = '@ViewBag.SaveUrl';

        var printObj = function (inObj, destx) {
            var dest = $('<table>').appendTo(destx),
                dst1 = $('<tr>').appendTo(dest),
                dst2 = $('<tr>').appendTo(dest);
            for (var p in inObj) {
                $('<th>', { text: p, css: { color: 'red', padding: '3px', background: '#dedede' } }).appendTo(dst1);
                $('<td>', { text: inObj[p] || 'null' }).appendTo(dst2);
            }
        };

        $('button.a').click(function () {
            var curr = $(this).next(),
                outR = {
                    Double: 12,
                    DoubleNull: 13,
                    Decimal: 14,
                    DecimalNull: 15,
                    Double2: 16,
                    Double2Null: 17,
                    Decimal2: 18,
                    Decimal2Null: 19,
                    Single: 20,
                    SingleNull: 21,
                    Float: 22,
                    FloatNull: 23,
                    Int: 24,
                    IntNull: 25,
                    Int64: 26,
                    Int64Null: 27
                };

            $('<hr />').appendTo(curr);
            printObj(outR, curr);

            $.ajax({
                type: 'POST',
                url: saveUrl,
                contentType: "application/json; charset=utf-8",
                dataType: 'json',

                data: $.toJSON({
                    inspectionFormID: 'fbde6eda-dde6-4ba9-b82d-3a35349415f0',

                    result: outR
                }),

                error: function (jqXHR, textStatus, errorThrown) {
                    alert('save failed');
                },

                success: function (data, textStatus, jqXHR) {
                    printObj(data, curr);
                }
            });
        });

        $('button.b').click(function () {
            var curr = $(this).next(),
                outR = {
                    Double: 12.0,
                    DoubleNull: 13.0,
                    Decimal: 14.0,
                    DecimalNull: 15.0,
                    Double2: 16.0,
                    Double2Null: 17.0,
                    Decimal2: 18.0,
                    Decimal2Null: 19.0,
                    Single: 20.0,
                    SingleNull: 21.0,
                    Float: 22.0,
                    FloatNull: 23.0,
                    Int: 24.0,
                    IntNull: 25.0,
                    Int64: 26.0,
                    Int64Null: 27.0
                };

            $('<hr />').appendTo(curr);
            printObj(outR, curr);

            $.ajax({
                type: 'POST',
                url: saveUrl,
                contentType: "application/json; charset=utf-8",
                dataType: 'json',

                data: $.toJSON({
                    inspectionFormID: 'fbde6eda-dde6-4ba9-b82d-3a35349415f0',

                    result: outR
                }),

                error: function (jqXHR, textStatus, errorThrown) {
                    alert('save failed');
                },

                success: function (data, textStatus, jqXHR) {
                    printObj(data, curr);
                }
            });
        });

        $('button.c').click(function () {
            var curr = $(this).next(),
                outR = {
                    Double: 12.5,
                    DoubleNull: 13.5,
                    Decimal: 14.5,
                    DecimalNull: 15.5,
                    Double2: 16.5,
                    Double2Null: 17.5,
                    Decimal2: 18.5,
                    Decimal2Null: 19.5,
                    Single: 20.5,
                    SingleNull: 21.5,
                    Float: 22.5,
                    FloatNull: 23.5,
                    Int: 24.5,
                    IntNull: 25.5,
                    Int64: 26.5,
                    Int64Null: 27.5
                };

            $('<hr />').appendTo(curr);
            printObj(outR, curr);

            $.ajax({
                type: 'POST',
                url: saveUrl,
                contentType: "application/json; charset=utf-8",
                dataType: 'json',

                data: $.toJSON({
                    'inspectionFormID': 'fbde6eda-dde6-4ba9-b82d-3a35349415f0',

                    'result': outR
                }),

                error: function (jqXHR, textStatus, errorThrown) {
                    alert('save failed');
                },

                success: function (data, textStatus, jqXHR) {
                    printObj(data, curr);
                }
            });
        });

        $('button.d').click(function () {
            var curr = $(this).next(),
                outR = {
                    Double:         '12',
                    DoubleNull:     '13',
                    Decimal:        '14',
                    DecimalNull:    '15',
                    Double2:        '16',
                    Double2Null:    '17',
                    Decimal2:       '18',
                    Decimal2Null:   '19',
                    Single:         '20',
                    SingleNull:     '21',
                    Float:          '22',
                    FloatNull:      '23',
                    Int:            '24',
                    IntNull:        '25',
                    Int64:          '26',
                    Int64Null:      '27'
                };

            $('<hr />').appendTo(curr);
            printObj(outR, curr);

            $.ajax({
                type: 'POST',
                url: saveUrl,
                contentType: "application/json; charset=utf-8",
                dataType: 'json',
                data: $.toJSON({
                    'inspectionFormID': 'fbde6eda-dde6-4ba9-b82d-3a35349415f0',

                    'result': outR
                }),

                error: function (jqXHR, textStatus, errorThrown) {
                    alert('save failed');
                },

                success: function (data, textStatus, jqXHR) {
                    printObj(data, curr);
                }
            });
    });

    $('button.e').click(function () {
        var curr = $(this).next(),
                outR = {
                    Double:         '12.5',
                    DoubleNull:     '13.5',
                    Decimal:        '14.5',
                    DecimalNull:    '15.5',
                    Double2:        '16.5',
                    Double2Null:    '17.5',
                    Decimal2:       '18.5',
                    Decimal2Null:   '19.5',
                    Single:         '20.5',
                    SingleNull:     '21.5',
                    Float:          '22.5',
                    FloatNull:      '23.5',
                    Int:            '24.5',
                    IntNull:        '25.5',
                    Int64:          '26.5',
                    Int64Null:      '27.5'
                };

        $('<hr />').appendTo(curr);
        printObj(outR, curr);

        $.ajax({
            type: 'POST',
            url: saveUrl,
            contentType: "application/json; charset=utf-8",
            dataType: 'json',
            data: $.toJSON({
                'inspectionFormID': 'fbde6eda-dde6-4ba9-b82d-3a35349415f0',

                'result': outR
            }),

            error: function (jqXHR, textStatus, errorThrown) {
                alert('save failed');
            },

            success: function (data, textStatus, jqXHR) {
                printObj(data, curr);
            }
        });
});
    });

</script>

Запустите его, нажмите каждую кнопку один раз и посмотрите на «до / после». Заранее благодарим за понимание, исправление или помощь, которую вы можете оказать для решения этой проблемы.

Вы также можете скачать примеры кода, перечисленные выше, и посмотреть официальный отчет об ошибке по этой ссылке: http://aspnet.codeplex.com/workitem/8114

<ч /> РЕДАКТИРОВАТЬ: я включаю это изображение, чтобы помочь всем понять, что здесь происходит

нажмите здесь, чтобы увидеть скриншот включенного примера с запущенным

В основном: {propertyThatIsADecimal: 54} становится {propertyThatIsADecimal: 0} на сервере для нескольких различных типов чисел в различных сценариях, которые, кажется, не имеют смысла или причины.

Ответы [ 4 ]

3 голосов
/ 27 июля 2011

Причина в том, что когда MVC встречает число, оно обрабатывает его как Int32. Как таковые, по какой-то причине нет конвертеров от Int32 до Decimal или Nullable<Int64>. Есть несколько способов обойти эту проблему. Строки, как у вас уже есть в вашем проекте или для создания пользовательского связывателя модели.

public class JsonTestModelBinder : IModelBinder {
    public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
        JsonTest result = new JsonTest();

        foreach (var property in typeof(JsonTest).GetProperties()) {
            //the value provider starts with the name of the property we're binding to
            //i'm not sure if this changed or not as i don't recall having to do this
            //before - you can remove "result." if your needs don't require it
            var value = bindingContext.ValueProvider.GetValue("result." + property.Name);
            if (value != null && value.RawValue != null) {
                //are we binding to a nullable?
                if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) {
                    property.SetValue(result, Convert.ChangeType(value.AttemptedValue, new NullableConverter(property.PropertyType).UnderlyingType), null);
                } else {
                    property.SetValue(result, Convert.ChangeType(value.AttemptedValue, property.PropertyType), null);
                }
            }
        }

        return result;
    }
}

Я не совсем уверен, почему мы все еще не можем конвертировать из Int32 в Decimal, но проблема существует в MVC ValueProviderResult.ConvertSimpleType. Он использует TypeDescriptor.GetConverter(your propertyType), и для этих типов нет доступных преобразований.

Мне не нравится этот конкретный метод, но пока он вам доступен.

0 голосов
/ 21 февраля 2011

Я скачал твой код и запустил пример.Когда я нажал кнопку, я получил ошибку JavaScript (ошибка времени выполнения Microsoft JScript: объект не поддерживает это свойство или метод) по адресу:

$.ajax({
            type: 'POST',
            url: saveUrl,
            contentType: "application/json; charset=utf-8",
            dataType: 'json',

            data: $.toJSON({
                inspectionFormID: 'fbde6eda-dde6-4ba9-b82d-3a35349415f0',

                result: outR
            }),

            error: function (jqXHR, textStatus, errorThrown) {
                alert('save failed');
            },

            success: function (data, textStatus, jqXHR) {
                printObj(data, curr);
            }
        });

Из вашего описания мне также не было понятно, что "до / после "должен был быть.Можете ли вы отнести это к абсолютно простому случаю только одним примером?Я предполагаю, что если мы сможем выяснить это для этого случая, то это будет относиться ко всем остальным.

0 голосов
/ 07 марта 2011

Я борюсь с этой же проблемой, но, к сожалению, я не могу выполнить какое-либо преобразование до сериализации.Я все еще должен найти решение.

Кроме того, это не просто обнуляемые типы и десятичные числа, которые не десериализованы для меня - пара строк равна нулю, и каждыйсвойство массива дочерних объектов также отображается как ноль.

Я подозревал, что это может быть проблема с десериализацией в прокси-класс Entity Framework POCO, но, очевидно, это не проблема после просмотра вашего кода.

0 голосов
/ 20 февраля 2011

I only посмотрел на ваш вопрос, поэтому я прошу прощения, если это не относится. Но я помню, что столкнулся с несколькими проблемами с Джсоном:

  1. Было ли это обязательным вопросом? Если это так, возможно, вы можете реализовать пользовательскую привязку. (Очевидно, MVC3 уже использует JsonFactory).
  2. Я не могу вспомнить проблему, но мне нужно было добавить JsonRequestBehaviour.AllowGet при выполнении вызовов jQuery с использованием .getJSON.
  3. Имеет ли значение, что у вас нет [HttpGet] или [HttpPost] в вашем методе Save?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...