有没有办法在没有空构造函数的情况下反序列化对象?

Is there a way to DeserializeObject without empty constructor?

我目前正在为我的 .net 核心启用不可为 null 的引用类型 api。

问题是集成测试在尝试反序列化对象时失败,因为它没有空构造函数。我不能有一个空的构造函数,因为这会使 属性: 可能为 null.

问题:

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 时有效,但测试失败:

测试

    [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),但 dog 为 null。

使用默认值填充 class。对于可为 null 的引用类型,默认值将为 null,因此在您的情况下,只需分配您期望的默认值即可。

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;
    }

}

序列化程序将构造一个新对象。该对象包含一个字符串成员,并且您已禁用可为 null 的引用类型。因此,您需要指定创建对象时的值。

如果你不想使用默认构造函数(一个空构造函数),你可以告诉序列化器你想使用的构造函数: https://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm

这是正确的行为 JsonConvert.DeserializeObject 使用构造函数只创建一个对象。它不会向它传递任何参数,这就是您收到错误的原因。

发生的事情是,dog 为空,因此您对 Name = dog.Name; 的赋值会抛出 NullreferenceException

在您的构造函数中使用它之前,理想情况下您应该检查 dog 是否为 null。

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

正如@CodeCaster 指出的那样,问题是荒谬的 - 但我找到了一个非最佳解决方案。

在我创建的测试项目中:

  public class TestDogViewModel : DogViewModel
    {

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

    }

反序列化时我使用了这个:

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

开发人员不会错误地使用此 viewModel,因为该项目没有对测试项目的引用,

所以现在如果你想要一个 DogViewModel,你需要有一只狗。这在我的实际项目中更有意义 :S.

来自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-to-immutable-classes-and-structs

也许这是一个解决方案