Есть ли способ DeserializeObject без пустого конструктора? - PullRequest
0 голосов
/ 20 февраля 2020

В настоящее время я работаю над включением необнуляемых ссылочных типов для моего. net core api.

Проблема в том, что при попытке выполнить десериализациюObject не происходит интеграция, поскольку в нем нет пустого конструктора. У меня не может быть пустого конструктора, потому что это сделает свойство: возможным пустым .

Проблема:

JsonConvert.DeserializeObject<ViewModel>(result); вызывает конструктор, который имеет конструктор с Аргумент объекта ViewModel(Model model). В тесте это ноль.

Я сделал простой пример:

API:

public class Dog
    {
        public string Name { get; set; } = null!;
    }

    public class DogViewModel
    {

        public DogViewModel(Dog dog)
        {
            Name = dog.Name;
        }

        public string Name { get; set; }
    }


    [HttpGet]
    [Route("dog")]
    public DogViewModel GetDog()
    {
        var dog = new Dog
        {
            Name = "Fido"
        };
        return new DogViewModel(dog);
    }

Это работает, когда я использую клиент вызывает API, но он не проходит тесты:

Test

    [Fact]
    public async Task GetDog()
    {

        var response = await Client.GetAsync("dog");

        var result = response.Content.ReadAsStringAsync().Result;
        Assert.True(response.IsSuccessStatusCode, result);
        var responeseAsObject = JsonConvert.DeserializeObject<DogViewModel>(result); <----- this is where it breaks
        Assert.IsType<string>(responeseAsObject.Name);
        Assert.NotNull(responeseAsObject);
    }

JsonConvert.DeserializeObject вызывает конструктор DogViewModel; DogViewModel (Dog dog), но собака нулевая.

Ответы [ 4 ]

1 голос
/ 20 февраля 2020

Заполните класс значениями по умолчанию. При нулевых ссылочных типах значение по умолчанию будет нулевым, поэтому в вашем случае просто присвойте ожидаемое значение по умолчанию.

public class ClassName
{
    public ClassName()
    {
        Name = string.Empty; //Default value
    }

    public ClassName(Dog dog)
    {
        Name = dog.Name;
    }

}

или

public class ClassName
{
    public string Name { get; set; } = string.Empty;

    public ClassName() {}

    public ClassName(Dog dog)
    {
        Name = dog.Name;
    }

}

Сериализатор создаст новый объект. Объект содержит строковый член, и вы отключили нулевые ссылочные типы. Таким образом, вам нужно указать, какое значение будет при создании объекта.

Если вы не хотите использовать конструктор по умолчанию (пустой конструктор), вы можете указать сериализатору конструктор, который вы хотели бы использовать. использование: https://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm

0 голосов
/ 14 апреля 2020

От docs.microsoft: атрибут Newtonsoft. Json [JsonConstructor] позволяет указать, какой конструктор вызывать при десериализации в POCO. System.Text. Json поддерживает только конструкторы без параметров. В качестве обходного пути вы можете вызвать любой конструктор, который вам нужен, в пользовательском конвертере. -> https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize для неизменных классов и структур

Может быть, это может быть решением

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

Вопрос, как отмечает @CodeCaster, бессмысленный - но я нашел неоптимальное решение.

В тестовом проекте я создал это:

  public class TestDogViewModel : DogViewModel
    {

        public TestDogViewModel() : base(new Dog {Name = ""})
        {
        }

    }

При десериализации i использовал это:

var responeseAsObject = JsonConvert.DeserializeObject<TestDogViewModel>(result);

Разработчики не будут использовать эту viewModel по ошибке, так как проект не имеет ссылки на тестовый проект,

Так что теперь, если вы хотите иметь DogViewModel вам нужно иметь собаку. В моем реальном проекте это имеет больше смысла: S.

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

Это правильное поведение JsonConvert.DeserializeObject использует конструктор, чтобы просто создать объект. Он не будет передавать ему никаких параметров, и поэтому вы получаете сообщение об ошибке.

Что происходит, dog равно нулю и, следовательно, ваше назначение Name = dog.Name; throws NullreferenceException.

В идеале вы должны проверить dog на ноль, прежде чем использовать его в своем конструкторе.

    public DogViewModel(Dog dog)
    {
        Name = dog?.Name;
    }
...