我试图想出一个可以检测属性何时设置为null的模式。类似Nullable<T>
类的东西,但更高级。我们称之为MoreThanNullable<T>
。基本上我需要根据以下3种情况做一些不同的事情:
我创建了自己的类来实现这个&#34;实例化&#34;属性,它都在测试场景中工作。一些示例代码:
public struct MoreThanNullable<T>
{
private bool hasValue;
internal T value;
public bool Instantiated { get; set; }
public MoreThanNullable(T value)
{
this.value = value;
this.hasValue = true;
Instantiated = true;
}
// ... etc etc...
按照我的预期通过测试:
[TestFixture]
public class MoreThanNullableTests
{
public class AccountViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public MoreThanNullable<string> Test1 { get; set; }
public MoreThanNullable<string> Test2 { get; set; }
public MoreThanNullable<string> Test3 { get; set; }
}
[Test]
public void Tests()
{
var myClass = new AccountViewModel();
Assert.AreEqual(false, myClass.Test1.Instantiated);
myClass.Test1 = null;
Assert.AreEqual(true, myClass.Test1.Instantiated);
}
}
接下来,使用相同的视图模型,我将其连接到我的REST服务上的POST,然后传入以下JSON:
{
Name:"test",
Test1:null,
Test2:"test"
}
......一切都崩溃了。 Test1和Test3都没有实例化!显然,我并不期望Test3被实例化,但Test1保持未实例化是出乎意料的。实际上,我无法区分我所做的财产与通过REST收到的财产之间的区别。 (注意:在我们的应用程序中,未设置和设置为null之间存在明显差异)
我做错了吗?或者这是Web API的正确行为吗?
** 更新 **
我可能没有说清楚的是我已经有效地复制了Nullable<T>
类来实现这一点,包括像这样重载隐式运算符:
public static implicit operator MoreThanNullable<T>(T value)
{
return new MoreThanNullable<T>(value);
}
我似乎总是在我的问题中遗漏重要内容......
答案 0 :(得分:2)
为什么不起作用?在你的JSON中
{
Name:"test",
Test1:null,
Test2:"test"
}
Test1结束Test2是原始类型,但是你创建了自己的类型,它们应该映射到它们的属性,因此它不再是原始的,而是一个复杂的对象。所以你的JSON实际上也应该包含对象。如果您将其更改为
,它将起作用{
Name:"test",
Test1:{Instantiated:false, value: null},
Test2:{Instantiated:false, value: "test"},
}
现在它们是对象并且反序列化。你的struct也有一个默认的空构造函数,如果它是一个带有私有默认构造函数的类,它将不再起作用。
现在,您如何获得像字符串这样的基本类型来反序列化到您的自定义类型?您可以使用自定义web api action filter执行此操作,并解析传入的json并将其映射到OnActionExecuting中的对象。
编辑2
假设您使用JSON.NET from newtonsoft,您可以插入自定义JSON转换器。这将是Web api应用程序的全局变更,无需自定义过滤器。
public sealed class MoreThanNullableConverter : Newtonsoft.Json.JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof (MoreThanNullable<>);
}
public override bool CanWrite { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// return a new MoreThanNullable instance with value
return Activator.CreateInstance(objectType, reader.Value);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
更改为webapiconfig.cs
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
jsonFormatter.SerializerSettings.Converters.Add(new MoreThanNullableConverter());
答案 1 :(得分:1)
我认为你已经陷入了.net
中服务反序列化的常见问题我总是在WCF看到这个问题,但我认为它也出现在json反序列化中
反序列化期间的.NET不会执行构造函数,所以如果你需要在反序列化后执行一些代码,你必须添加一些代码作为这些[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
// ... logic here after deserialization
}
在该逻辑中,您应该填写缺少信息的私人字段。 必须根据值是否为null来设置hasValue字段。 实例化应该有一个私有字段与setter私有。它应该建立在真正的公共价值观上。 如果hasValue为true,则实例化显然是正确的,否则怎么办? 所以问题是,我们怎样才能将hasvalue假,但是实例化为真?
这是唯一一种无法用其他值确定值的情况,因此我们需要其他的东西。使实例化公共的setter是最简单的方法,但为了确保对象一致性,你应该在反序列化后检查它以确保以前的对象规则......