Json.net`JsonConstructor`构造函数参数名称 [英] Json.net `JsonConstructor` constructor parameter names

查看:64
本文介绍了Json.net`JsonConstructor`构造函数参数名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当通过JsonConstructor使用特定的.ctor来反序列化IList<ISomeInterface>属性时,参数名称必须与原始 Json名称匹配,并且不使用这些属性上的JsonProperty映射. /p>

示例:

SpokenLanguages参数始终为空,因为它与spoken_languages不匹配,但是有一个JsonProperty映射它:

public partial class AClass : ISomeBase
{
    public AClass() { }

    [JsonConstructor]
    public AClass(IList<SysType> SysTypes, IList<ProductionCountry> production_countries, IList<SpokenLanguage> SpokenLanguages)
    {
        this.Genres = SysTypes?.ToList<IGenre>();
        this.ProductionCountries = production_countries?.ToList<IProductionCountry>();
        this.SpokenLanguages = SpokenLanguages?.ToList<ISpokenLanguage>();
    }

    public int Id { get; set; }
    public IList<IGenre> Genres { get; set; }
    [JsonProperty("production_countries")]
    public IList<IProductionCountry> ProductionCountries { get; set; }
    [JsonProperty("spoken_languages")]
    public IList<ISpokenLanguage> SpokenLanguages { get; set; }
}

这仅仅是Json.Net调用构造函数的限制",还是我缺少的东西.

仅供参考:我正在通过Rosyln生成所有代码,而不是为此生成每种类型的JsonConverter ...

解决方案

当Json.NET调用参数化的构造函数时,它将按名称将JSON属性与构造函数参数进行匹配.但是,对于也对应于类型成员的JSON属性,它使用的名称是-成员名称,还是

  • 构造函数参数IList<SpokenLanguage> SpokenLanguages与反映的名称匹配,而不是覆盖的属性名称:

    [JsonProperty("spoken_languages")]
    public IList<ISpokenLanguage> SpokenLanguages { get; set; }
    

  • IList<SysType> SysTypes都不匹配(这是问题中的错字吗?)

  • 但是,重要的是JSON文件本身中的属性名称​​ 构造函数参数名称,如的值匹配,而构造函数自变量SpokenLanguages匹配名为"spoken_languages"的JSON值.

    那么,如何成功反序列化您的类型?首先,您可以用[JsonProperty(overrideName)]标记构造函数参数,以覆盖反序列化期间使用的构造函数名称:

    public partial class AClass : ISomeBase
    {
        public AClass() { }
    
        [JsonConstructor]
        public AClass([JsonProperty("Genres")] IList<SysType> SysTypes, IList<ProductionCountry> production_countries, [JsonProperty("spoken_languages")] IList<SpokenLanguage> SpokenLanguages)
        {
            this.Genres = SysTypes == null ? null : SysTypes.Cast<IGenre>().ToList();
            this.ProductionCountries = production_countries == null ? null : production_countries.Cast<IProductionCountry>().ToList();
            this.SpokenLanguages = SpokenLanguages == null ? null : SpokenLanguages.Cast<ISpokenLanguage>().ToList();
        }
    

    第二,由于您似乎正在使用构造函数反序列化包含作为具体对象的接口的集合中的项目,因此您可以考虑使用基于 ItemConverter :

    public partial class AClass : ISomeBase
    {
        public AClass() { }
    
        public int Id { get; set; }
    
        [JsonProperty(ItemConverterType = typeof(CustomCreationConverter<IGenre, SysType>))]
        public IList<IGenre> Genres { get; set; }
    
        [JsonProperty("production_countries", ItemConverterType = typeof(CustomCreationConverter<IProductionCountry, ProductionCountry>))]
        public IList<IProductionCountry> ProductionCountries { get; set; }
    
        [JsonProperty("spoken_languages", ItemConverterType = typeof(CustomCreationConverter<ISpokenLanguage, SpokenLanguage>))]
        public IList<ISpokenLanguage> SpokenLanguages { get; set; }
    }
    
    public class CustomCreationConverter<T, TSerialized> : CustomCreationConverter<T> where TSerialized : T, new()
    {
        public override T Create(Type objectType)
        {
            return new TSerialized();
        }
    }
    

    示例小提琴显示了这两个选项.

    When using a specific .ctor via JsonConstructor for deserializing IList<ISomeInterface> properties, the parameter names must match the original Json names and the JsonProperty mapping on those properties are not used.

    Example:

    SpokenLanguages parameter is always null since it does not match spoken_languages, but there is a JsonProperty mapping it:

    public partial class AClass : ISomeBase
    {
        public AClass() { }
    
        [JsonConstructor]
        public AClass(IList<SysType> SysTypes, IList<ProductionCountry> production_countries, IList<SpokenLanguage> SpokenLanguages)
        {
            this.Genres = SysTypes?.ToList<IGenre>();
            this.ProductionCountries = production_countries?.ToList<IProductionCountry>();
            this.SpokenLanguages = SpokenLanguages?.ToList<ISpokenLanguage>();
        }
    
        public int Id { get; set; }
        public IList<IGenre> Genres { get; set; }
        [JsonProperty("production_countries")]
        public IList<IProductionCountry> ProductionCountries { get; set; }
        [JsonProperty("spoken_languages")]
        public IList<ISpokenLanguage> SpokenLanguages { get; set; }
    }
    

    Is this just a "limitation" of how Json.Net calls the constructor or is there something I am missing.

    FYI: I am code generating all this via Rosyln and am not looking at generating a JsonConverter for each type for this...

    解决方案

    When Json.NET invokes a parameterized constructor, it matches JSON properties to constructor arguments by name. However, for JSON properties that also correspond to type members, which name does it use - the member name, or the override type member name specified by JsonPropertyAttribute.PropertyName?

    It appears you are hoping it matches on both, since your argument naming conventions are inconsistent:

    • The constructor argument production_countries matches the overridden property name:

      [JsonProperty("production_countries")]
      public IList<IProductionCountry> ProductionCountries { get; set; }
      

    • The constructor argument IList<SpokenLanguage> SpokenLanguages matches the reflected name rather than the overridden property name:

      [JsonProperty("spoken_languages")]
      public IList<ISpokenLanguage> SpokenLanguages { get; set; }
      

    • IList<SysType> SysTypes matches neither (is this a typo in the question?)

    However, what matters is the property name in the JSON file itself and the constructor argument name as shown in JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(). A simplified version of the algorithm is as follows:

    1. The property name is read from the JSON file.
    2. A closest match constructor argument is found (if any).
    3. A closest match member name is found (if any).
    4. If the JSON property matched a constructor argument, deserialize to that type and pass into the constructor,
    5. But if not, deserialize to the appropriate member type and set the member value after construction.

    (The implementation becomes complex when a JSON property matches both and developers expect that, for instance, [JsonProperty(Required = Required.Always)] added to the member should be respected when set in the constructor.)

    Thus the constructor argument production_countries will match a value named "production_countries" in the JSON, while the constructor argument SpokenLanguages will not match a JSON value named "spoken_languages".

    So, how to deserialize your type successfully? Firstly, you could mark the constructor parameters with [JsonProperty(overrideName)] to override the constructor name used during deserialization:

    public partial class AClass : ISomeBase
    {
        public AClass() { }
    
        [JsonConstructor]
        public AClass([JsonProperty("Genres")] IList<SysType> SysTypes, IList<ProductionCountry> production_countries, [JsonProperty("spoken_languages")] IList<SpokenLanguage> SpokenLanguages)
        {
            this.Genres = SysTypes == null ? null : SysTypes.Cast<IGenre>().ToList();
            this.ProductionCountries = production_countries == null ? null : production_countries.Cast<IProductionCountry>().ToList();
            this.SpokenLanguages = SpokenLanguages == null ? null : SpokenLanguages.Cast<ISpokenLanguage>().ToList();
        }
    

    Secondly, since you seem to be using the constructor to deserialize items in collections containing interfaces as concrete objects, you could consider using a single generic converter based on CustomCreationConverter as an ItemConverter:

    public partial class AClass : ISomeBase
    {
        public AClass() { }
    
        public int Id { get; set; }
    
        [JsonProperty(ItemConverterType = typeof(CustomCreationConverter<IGenre, SysType>))]
        public IList<IGenre> Genres { get; set; }
    
        [JsonProperty("production_countries", ItemConverterType = typeof(CustomCreationConverter<IProductionCountry, ProductionCountry>))]
        public IList<IProductionCountry> ProductionCountries { get; set; }
    
        [JsonProperty("spoken_languages", ItemConverterType = typeof(CustomCreationConverter<ISpokenLanguage, SpokenLanguage>))]
        public IList<ISpokenLanguage> SpokenLanguages { get; set; }
    }
    
    public class CustomCreationConverter<T, TSerialized> : CustomCreationConverter<T> where TSerialized : T, new()
    {
        public override T Create(Type objectType)
        {
            return new TSerialized();
        }
    }
    

    Example fiddle showing both options.

    这篇关于Json.net`JsonConstructor`构造函数参数名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆