【问题标题】:Is it possible to determine which properties were deserialised in Json.Net? [duplicate]是否可以确定在 Json.Net 中反序列化了哪些属性? [复制]
【发布时间】:2021-01-27 17:12:12
【问题描述】:

假设我有以下 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 中包含哪些属性,哪些属性被省略了?

在这个例子中,我的所有属性都可以为空,JSON 不包含 height 属性,所以我的 C# 对象最终会得到一个 null Height

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

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

所以我的问题是:我是否可以确定是否提供了值但null,或者未提供值因此默认为null。是否有一些可用的元数据在某处/以某种方式为我提供了这些信息?

这是在 ApiController 中使用的,因此反序列化是由 Formatter 自动完成的,但这是我当前的 formatter 设置:

private static void AddFormatter(HttpConfiguration config)
{
    var formatter = config.Formatters.JsonFormatter;

    formatter.SerializerSettings = new JsonSerializerSettings
    {
        Formatting       = Formatting.Indented,
        TypeNameHandling = TypeNameHandling.None
    };
}

【问题讨论】:

  • 在业务逻辑方面,不提供高度和提供为空的高度有什么区别?
  • 这是我用来更新一些其他数据的示例中间对象。最好我只想在初始 JSON 中提供的最终对象上设置字段。
  • @dbc 同意,感谢您的发现!

标签: c# asp.net-web-api json.net .net-framework-4.8


【解决方案1】:

您可以使用DefaultValueHandling 属性定义处理可空值的策略,如[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)],您可以阅读更多选项here.

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

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

}

【讨论】:

  • 不确定这如何帮助我实现目标...?
  • 尝试添加默认值,更新我的答案
【解决方案2】:

由于缺乏响应,除非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
}

【讨论】:

    猜你喜欢
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多