是否可以确定哪些属性在Json.Net中被取消了序列化



假设我有以下JSON:

{
"name": "Jim",
"age": 20
}

我将其取消序列化为以下C#对象:

public class Person
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("age")]
public int? Age    { get; set; }
[JsonProperty("height")]
public int? Height { get; set; }
}

有什么方法可以确定哪些属性包含在原始JSON中,哪些被省略了?

在这个例子中,我的所有属性都可以为null,JSON不包括height属性,所以我的C#对象将以nullHeight结束。

然而,用户也可以简单地提供null作为高度,例如

{
"name": "Jim",
"age": 20,
"height": null
}

所以我的问题是:我是否可以确定是否提供了值,但提供了null,或者没有提供,因此默认为null。有没有一些元数据可以提供给我这些信息?

这是在ApiController中使用的,所以反序列化是由格式化程序自动完成的,但这是我当前的格式化程序设置:

private static void AddFormatter(HttpConfiguration config)
{
var formatter = config.Formatters.JsonFormatter;
formatter.SerializerSettings = new JsonSerializerSettings
{
Formatting       = Formatting.Indented,
TypeNameHandling = TypeNameHandling.None
};
}

您可以使用DefaultValueHandling属性来定义处理[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]等可为null值的策略,您可以在此处阅读更多选项。

尝试添加用户无法提供的默认值,然后您就会知道该值是否由用户提供。

public class Person
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("age")]
public int? Age    { get; set; }
[DefaultValue(-1)] 
[JsonProperty("height", DefaultValueHandling = DefaultValueHandling.Populate)]
public int? Height { get; set; }

}

由于缺乏响应,除了svyatis.lviv提出的有用但并不出色的建议外,我决定以一种简单的非串行化相关方式实现它。

首先我做了这个界面:

public interface IPropertyChangeLog
{
IEnumerable<string> PropertiesChanged { get; }
void Reset();
}

然后我制作了这个助手类:

public class PropertyChangeLog<TSource> : IPropertyChangeLog
where TSource : IPropertyChangeLog
{
private readonly List<string> _changes = new List<string>();
public void UpdateProperty<TValue>(TValue newValue, ref TValue oldValue, [CallerMemberName] string propertyName = null)
{
oldValue = newValue;
_changes.Add(propertyName);
}
public IEnumerable<string> PropertiesChanged => _changes;
public void Reset() => _changes.Clear();
}

最后,我更新了Person类如下:

public class Person : IPropertyChangeLog
{
private PropertyChangeLog<Person> _log = new PropertyChangeLog<Person>();
private string _name;
private int? _age;
private int? _height;
[JsonProperty("name")]
public string Name
{
get => _name;
set => _log.UpdateProperty(value, ref _name);
}
[JsonProperty("age")]
public int? Age
{
get => _age;
set => _log.UpdateProperty(value, ref _age);
}
[JsonProperty("height")]
public int? Height
{
get => _height;
set => _log.UpdateProperty(value, ref _height);
}
IEnumerable<string> IPropertyChangeLog.PropertiesChanged => _log.PropertiesChanged;
void IPropertyChangeLog.Reset() => _log.Reset();
}

它比我想要的要详细一点,但它仍然非常简单易读。

为了使用它:

var person = JsonConvert.DeserializeObject<Person>("{ "name": "test" }");
var log = (IPropertyChangeLog)person;
// log.PropertiesChanged should now contain 'Name'
foreach (var property in log.PropertiesChanged)
{
// we know that the property named in 'property' changed
}
log.Reset();
JsonConvert.PopulateObject("{ "age": null }", person);
// now log.PropertiesChanged should only contain 'Age'
foreach (var property in log.PropertiesChanged)
{
// we know that the property named in 'property' changed
}

最新更新