Как вы передаете производный класс JObject в теле веб-API

Если у меня есть класс

public class TestMe : JObject
{

}

Как использовать его в методе API?

public async Task<IHttpActionResult> UpdateMe(int accountId, [FromBody] TestMe sometest)
{
  Console.WriteLine(sometest);
  ...

Когда я пытаюсь опубликовать что-нибудь для тела, например:

{
  "Bye": "string"
}

Я получаю следующую ошибку еще до того, как будет достигнута строка Console.WriteLine в методе UpdateMe:

"The parameters dictionary contains an invalid entry for parameter 'sometest' for method 'System.Threading.Tasks.Task`1[System.Web.Http.IHttpActionResult] UpdateMe(Int32, TestMe)' in '<somepath>.Controllers.MiscController'. The dictionary contains a value of type 'Newtonsoft.Json.Linq.JObject', but the parameter requires a value of type 'TestMe'."

person Post Impatica    schedule 29.01.2021    source источник
comment
Почему вы расширяете JObject?   -  person ShanieMoonlight    schedule 01.02.2021
comment
Один метод, который я иногда использую при работе с общими объектами Json, заключается в том, чтобы обрабатывать их как IDictionary‹string, object›. Другой способ может состоять в том, чтобы просто прочитать необработанное тело запроса в виде строки и десериализовать его самостоятельно. Возможно, вы могли бы достичь того, чего хотите, с помощью привязки модели, но это, вероятно, потребует гораздо больше работы.   -  person Driven-it    schedule 01.02.2021
comment
Существует множество типов данных, подходящих для получения динамических данных. JObject не является одним из них. Вы связываете свой API с конкретным объектом библиотеки JSON. Просто используйте динамический, или словарь, или любой другой тип, который вам больше подходит.   -  person FarhadGh    schedule 02.02.2021
comment
Извините, что вы принимаете мой ответ, но дали награду другому ответу. Это правильно?   -  person maytham-ɯɐɥʇʎɐɯ    schedule 02.02.2021
comment
@maytham-ɯɐɥʇʎɐɯ да, это был первый раз, когда я когда-либо делал награду, и рядом с обоими вашими ответами была кнопка награды, поэтому я думал, что смогу наградить обоих, но после того, как я наградил первый ответ , ваша кнопка награды исчезла :(   -  person Post Impatica    schedule 03.02.2021
comment
спасибо, грустно не получить награду в 250, но это нормально, что другой парень ее получит. спасибо, что сообщили мне ????   -  person maytham-ɯɐɥʇʎɐɯ    schedule 03.02.2021


Ответы (2)


Краткий ответ НЕВОЗМОЖЕН, IMO то, о чем вы просите, НЕВОЗМОЖНО и не имеет ничего общего с API.

ИМО у вас есть 3 варианта либо используйте

  • динамический тип
  • пользовательский тип объекта
  • Тип словаря

Как упоминается в этом ответе,

Какова бы ни была причина, по которой вы хотите это сделать, причина проста: JObject реализует IDictionary, и Json.NET обрабатывает этот случай особым образом. Если ваш класс реализует IDictionary, Json.NET не будет просматривать свойства вашего класса, а вместо этого будет искать ключи и значения в словаре. Итак, чтобы исправить ваше дело, вы можете сделать это

Давайте узнаем некоторые подробности:

Чтобы доказать мою первую точку зрения, попробуйте просто создать простое консольное приложение с newton.json с вашим вводом в виде строки:

var input = "{\"Bye\": \"string\"}";

С динамическим он будет работать нормально:

var result = JsonConvert.DeserializeObject<dynamic>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Теперь с настройкой объекта, например:

public class TestMe
{
    public string Bye { get; set; }
}

а также

var result = JsonConvert.DeserializeObject<TestMe>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Тоже отлично работает.

Теперь давайте рассмотрим ваш подход:

public class TestMe : JObject
{
    
}

Тестируя его следующим образом, он сломается:

var result = JsonConvert.DeserializeObject<TestMe>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Теперь давайте попробуем со словарем:

public class TestMe
{
    [JsonExtensionData]
    public Dictionary<string, JToken> MyProperties { get; set; } = new Dictionary<string, JToken>();
}

И проверить это

var result = JsonConvert.DeserializeObject<TestMe>(input);
Console.WriteLine(JsonConvert.SerializeObject(result));

Уилл тоже работает нормально.

Сейчас я представил 3 варианта.

person maytham-ɯɐɥʇʎɐɯ    schedule 01.02.2021

контроллер не может принять класс, который наследуется непосредственно от класса JObject. (дополнительная информация здесь). Я предполагаю, что в вашем случае вы не знаете, какие свойства вы получите во входном JSON. Это возможные решения:

Примите только класс JObject в вашем контроллере.

[Route("{accountID}")]
[HttpPost]
public async Task<IHttpActionResult> UpdateMe(int accountId, [FromBody] JObject sometest)
{
    string test = sometest.ToString();

    return Ok(test);
}

вы можете работать с классом JObject по своему усмотрению. Пример. подробнее читайте здесь.
Это одно из решений. к проблеме.


Другим решением будет чтение тела JSON как необработанного JSON и выполнение его в соответствии с вашими потребностями:

[Route("{accountID}")]
[HttpPost]
public async Task<IHttpActionResult> UpdateMe(int accountId)
{
    string rawContent = string.Empty;
    using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
    {
        contentStream.Seek(0, SeekOrigin.Begin);
        using (var sr = new StreamReader(contentStream))
        {
            rawContent = sr.ReadToEnd();
            // use raw content here
        }
    }

    return Ok(rawContent);
}

Другое решение для неизвестного входного JSON — принять тип dynamic в контроллере, но лично я этого не рекомендую. Как это реализовать читайте здесь . Почему мне это не нравится, проверьте это здесь.


person G.Dimov    schedule 02.02.2021