Подтвердить что ты не робот

Как использовать Json.NET для JSON-моделирования в проекте MVC5?

Я искал в Интернете ответ или пример, но еще не нашел его. Я просто хотел бы поменять сериализатор JSON по умолчанию, который используется для десериализации JSON при привязке модели к библиотеке JSON.NET.

Я нашел этот сообщение SO, но не могу его реализовать до сих пор, я даже не вижу пространство имен System.Net.Http.Formatters, и я не вижу GlobalConfiguration.

Что мне не хватает?

ОБНОВЛЕНИЕ

У меня есть проект ASP.NET MVC, это был в основном проект MVC3. В настоящее время я нацелен на .NET 4.5 и использует ASP.NET MVC 5 и связанные с ним пакеты NuGet.

Я не вижу сборки System.Web.Http или любое подобное пространство имен. В этом контексте я хотел бы добавить JSON.NET для использования в качестве стандартного связующего объекта для запросов типа JSON.

4b9b3361

Ответ 1

Наконец-то я нашел ответ. В принципе мне не нужен материал MediaTypeFormatter, который не предназначен для использования в среде MVC, но в ASP.NET Web API, поэтому я не вижу этих ссылок и пространств имен (кстати, они включены в Microsoft.AspNet.WeApi пакет NuGet).

Решение состоит в использовании поставщика пользовательских значений factory. Вот требуемый код.

    public class JsonNetValueProviderFactory : ValueProviderFactory
    {
        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            // first make sure we have a valid context
            if (controllerContext == null)
                throw new ArgumentNullException("controllerContext");

            // now make sure we are dealing with a json request
            if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
                return null;

            // get a generic stream reader (get reader for the http stream)
            var streamReader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
            // convert stream reader to a JSON Text Reader
            var JSONReader = new JsonTextReader(streamReader);
            // tell JSON to read
            if (!JSONReader.Read())
                return null;

            // make a new Json serializer
            var JSONSerializer = new JsonSerializer();
            // add the dyamic object converter to our serializer
            JSONSerializer.Converters.Add(new ExpandoObjectConverter());

            // use JSON.NET to deserialize object to a dynamic (expando) object
            Object JSONObject;
            // if we start with a "[", treat this as an array
            if (JSONReader.TokenType == JsonToken.StartArray)
                JSONObject = JSONSerializer.Deserialize<List<ExpandoObject>>(JSONReader);
            else
                JSONObject = JSONSerializer.Deserialize<ExpandoObject>(JSONReader);

            // create a backing store to hold all properties for this deserialization
            var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            // add all properties to this backing store
            AddToBackingStore(backingStore, String.Empty, JSONObject);
            // return the object in a dictionary value provider so the MVC understands it
            return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);
        }

        private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
        {
            var d = value as IDictionary<string, object>;
            if (d != null)
            {
                foreach (var entry in d)
                {
                    AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
                }
                return;
            }

            var l = value as IList;
            if (l != null)
            {
                for (var i = 0; i < l.Count; i++)
                {
                    AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
                }
                return;
            }

            // primitive
            backingStore[prefix] = value;
        }

        private static string MakeArrayKey(string prefix, int index)
        {
            return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
        }

        private static string MakePropertyKey(string prefix, string propertyName)
        {
            return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
        }
    }

И вы можете использовать его в своем методе Application_Start:

// remove default implementation    
ValueProviderFactories.Factories.Remove(ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().FirstOrDefault());
// add our custom one
ValueProviderFactories.Factories.Add(new JsonNetValueProviderFactory());

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

Ответ 2

У меня была такая проблема и с этим. Я отправлял JSON в действие, но мои имена JsonProperty были проигнорированы. Таким образом, мои свойства модели всегда были пустыми.

public class MyModel
{
    [JsonProperty(PropertyName = "prop1")]
    public int Property1 { get; set; }

    [JsonProperty(PropertyName = "prop2")]
    public int Property2 { get; set; }

    [JsonProperty(PropertyName = "prop3")]
    public int Property3 { get; set; }

    public int Foo { get; set; }
}

Я отправляю в действие с помощью этой пользовательской функции jquery:

(function ($) {
    $.postJSON = function (url, data, dataType) {

        var o = {
            url: url,
            type: 'POST',
            contentType: 'application/json; charset=utf-8'
        };

        if (data !== undefined)
            o.data = JSON.stringify(data);

        if (dataType !== undefined)
            o.dataType = dataType;

        return $.ajax(o);
    };
}(jQuery));

И я называю это следующим образом:

data = {
    prop1: 1,
    prop2: 2,
    prop3: 3,
    foo: 3,
};

$.postJSON('/Controller/MyAction', data, 'json')
            .success(function (response) {
                ...do whatever with the JSON I got back
            });

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

[HttpPost]
public JsonNetResult MyAction(MyModel model)
{
    ...
}

Решение оказалось довольно простым

Я только что реализовал общую версию привязки модели Dejan, которая работает очень хорошо для меня. Вероятно, он мог бы использовать некоторые фиктивные проверки (например, убедиться, что запрос на самом деле является приложением /json ), но он делает трюк прямо сейчас.

internal class JsonNetModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        controllerContext.HttpContext.Request.InputStream.Position = 0;
        var stream = controllerContext.RequestContext.HttpContext.Request.InputStream;
        var readStream = new StreamReader(stream, Encoding.UTF8);
        var json = readStream.ReadToEnd();
        return JsonConvert.DeserializeObject(json, bindingContext.ModelType);
    }
}

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

[HttpPost]
public JsonNetResult MyAction([ModelBinder(typeof(JsonNetModelBinder))] MyModel model)
{
    ...
}

Теперь мои атрибуты [JsonProperty (PropertyName = ""]] больше не игнорируются в MyModel, и все связано правильно!

Ответ 3

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

Поскольку мои объекты уже были должным образом аннотированы для работы с Json.NET, я выбрал другой маршрут: я применил настраиваемое связующее устройство и использовал Json.NET для явного десериализации данных тела запроса следующим образом:

internal class CustomModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // use Json.NET to deserialize the incoming Position
        controllerContext.HttpContext.Request.InputStream.Position = 0; // see: http://stackoverflow.com/a/3468653/331281
        Stream stream = controllerContext.RequestContext.HttpContext.Request.InputStream;
        var readStream = new StreamReader(stream, Encoding.UTF8);
        string json = readStream.ReadToEnd();
        return JsonConvert.DeserializeObject<MyClass>(json, ...);
    }
}

Вкладка Global.asax.cs зарегистрирована в Global.asax.cs:

  ModelBinders.Binders.Add(typeof(MyClass), new CustomModelBinder();