在 C# 中,如何在运行时设置、重置或定义 JsonProperty 属性?

Posted

技术标签:

【中文标题】在 C# 中,如何在运行时设置、重置或定义 JsonProperty 属性?【英文标题】:In C#, how can I set, reset, or define a JsonProperty attribute at runtime? 【发布时间】:2021-09-27 20:29:45 【问题描述】:

我们有一个用 C# 开发的微服务,需要与 Salesforce 集成。

我们正在实施一个新的 Saleforce 实例,出于某种愚蠢的原因,有人决定将一个字段从 My_Field__c 重命名为 MyField__c,他们不希望任何自定义字段带有任何可避免的下划线。愚蠢,但不是我的选择。

现在我需要将一个新的微服务实例集成到新的 Salesforce 实例中,使解决方案只有一个字符不同,但我们当然需要能够同时维护原始微服务和新微服务。

在理想情况下,我只需在 appSettings 中设置一些值,然后在我的 [JsonProperty(PropertyName = "My_Field__c")] 声明中使用它,但该属性当然需要编译时间常数,所以我们不能做这么简单的事情。

创建一个自定义序列化器/反序列化器或维护一个不同的 git 分支来删除这个字符感觉有点矫枉过正。

有什么方法可以动态设置这个属性吗?

(有人建议TypeDescriptor 可能提供解决方案,但我找不到有关如何应用它的详细信息。)

【问题讨论】:

将它们都添加到您的模型类中,以及另一个返回包含值的属性? 和上面一样,你可以在一个模型中同时拥有这两种,不同的消费者会忽略没有在他们的 json 模式中定义的属性 @Max,我不确定你把它们都加起来是什么意思?我可以在同一个属性上堆叠两个相同的属性吗? @Fabio,如果我直接与 Salesforce 通信,也许额外的属性将被忽略,但我们有一个缓存系统来批量处理位于两者之间的对 Salesforce 的 API 调用并且出于安全原因,它不允许出现意外值... 另一种方法是有两个不同的模型,并在运行时创建不同的实例,然后再发送数据。 【参考方案1】:

为新旧属性名称添加两个单独的属性,以及一个返回已反序列化的辅助属性:

public class MyModel

   [JsonProperty(PropertyName = "My_Field__c")]
   public string MyFieldOld  get; set; 

   [JsonProperty(PropertyName = "MyField__c")]
   public string MyFieldNew  get; set; 

   [JsonIgnore]
   public string MyField => MyFieldOld ?? MyFieldNew;


【讨论】:

但我们有一个缓存系统来批量处理位于两者之间的对 Salesforce 的 API 调用,并且出于安全原因,它不允许出现意外值 OP 你能详细说明一下缓存系统吗?可以修改吗? @Max,我们有一个微服务,也是用 C# 开发的。它旨在促进数据流入和流出,最大限度地减少它实际需要了解的有关 Salesforce 的信息量,同时也最大限度地减少它对 Salesforce 内置数据保护的破坏程度(它仍然会妥协,但比完全忽略了它正在处理的字段)。 Salesforce 本身通常会拒绝有关其无法识别的数据的请求。我认为在不产生更新、更复杂的复杂性的情况下修改缓存系统的好方法。【参考方案2】:

我试图让TypeDescriptor.AddAttributes() 使用该类型,但这不起作用。由于实例在反序列化之前不存在,因此尝试将其与实例一起使用是不可能的。

但是,我想出了如何使用 Contract Resolver 来完成这项工作:


public static MyModel FromSerializedMyModel(string content, SalesforceFieldNames salesforceFieldNames) =>
   JsonConvert.DeserializeObject<MyModel>(contents, 
       CreateSettings(salesforceFieldNames));

private static JsonSerializerSettings CreateSettings(SalesforceFieldNames salesforceFieldNames) => new()
        
            ContractResolver = new MyModelContractResolver(salesforceFieldNames)
        ;



class MyModelContractResolver: DefaultContractResolver
    
        private readonly SalesforceFieldNames _salesforceFieldNames;

        public MyModelContractResolver(SalesforceFieldNames salesforceFieldNames) =>
            _salesforceFieldNames = salesforceFieldNames;

        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        
            IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
            Dictionary<string, JsonProperty> propertiesByUnderlyingName = properties.ToDictionary(x => x.UnderlyingName, x => x);
            if (propertiesByUnderlyingName.TryGetValue("MyField", out JsonProperty spvTagProperty))
            
                spvTagProperty.PropertyName = _salesforceFieldNames.MyField;
            
            return properties;
        
    

有点冗长和丑陋,但它有效......

【讨论】:

以上是关于在 C# 中,如何在运行时设置、重置或定义 JsonProperty 属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# WPF(运行时)中将 Dependency Property propertymetadata 设置为 LinearGradientBrush?

在 C# 中对某个数据库或表运行 SQL 脚本?

快速设置重置我的自定义按钮的状态

C# dataGridView1 重置背景颜色?

如何在Visual Studio中选择C++和C#的编译器版本

如何在 C# 中使用 TaskScheduler 设置“仅在登录时运行”和“运行方式”?