有没有办法在没有空构造函数的情况下反序列化对象?
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
也许这是一个解决方案
我目前正在为我的 .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
也许这是一个解决方案