【问题标题】:Json.net `JsonConstructor` constructor parameter namesJson.net `JsonConstructor` 构造函数参数名称
【发布时间】:2021-03-06 22:45:40
【问题描述】:

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

示例:

SpokenLanguages 参数始终为 null,因为它与 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...

【问题讨论】:

    标签: c# json.net


    【解决方案1】:

    当 Json.NET 调用参数化构造函数时,它会按名称将 JSON 属性与构造函数参数匹配,使用顺序忽略大小写匹配。但是,对于也对应于类型成员的 JSON 属性,它使用哪个名称 - 成员名称,还是 JsonPropertyAttribute.PropertyName 指定的覆盖类型成员名称?

    您似乎希望它在 both 上匹配,因为您的参数命名约定不一致:

    • 构造函数参数production_countries 匹配被覆盖的属性名称:

       [JsonProperty("production_countries")]
       public IList<IProductionCountry> ProductionCountries { get; set; }
      
    • 构造函数参数IList&lt;SpokenLanguage&gt; SpokenLanguages匹配反射名称而不是覆盖的属性名称:

       [JsonProperty("spoken_languages")]
       public IList<ISpokenLanguage> SpokenLanguages { get; set; }
      
    • IList&lt;SysType&gt; SysTypes 都不匹配(这是问题中的错字吗?)

    然而,重要的是 JSON 文件本身中的属性名称构造函数参数名称,如 JsonSerializerInternalReader.ResolvePropertyAndCreatorValues() 所示。该算法的简化版本如下:

    1. 从 JSON 文件中读取属性名称。
    2. 找到最接近匹配的构造函数参数(如果有)。
    3. 找到最匹配的成员名称(如果有)。
    4. 如果 JSON 属性与构造函数参数匹配,则反序列化为该类型并传递给构造函数,
    5. 但如果不是,则反序列化为适当的成员类型,并在构造后设置成员值。

    (当 JSON 属性与 both 匹配时,实现变得复杂,并且开发人员希望,例如,添加到成员的 [JsonProperty(Required = Required.Always)] 在构造函数中设置时应该得到尊重。)

    因此,构造函数参数 production_countries 将匹配 JSON 中名为 "production_countries" 的值,而构造函数参数 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();
        }
    

    其次,由于您似乎使用构造函数将包含接口的集合中的项目反序列化为具体对象,您可以考虑使用基于CustomCreationConverter 的单个通用转换器作为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 显示两个选项。

    【讨论】:

    • 谢谢,一个非常好的答案!我正在将接口集合转换为具体类型,以便将它们存储在领域 ORM 中(领域不处理接口协方差)。 ItemConverter 示例实际上是我真正需要的,而不是使用自定义 JsonConstructor .actor.... 了解了有关 Json.net 的新知识,再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-31
    • 2015-07-25
    • 2014-09-29
    • 2012-10-04
    • 1970-01-01
    相关资源
    最近更新 更多