在 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?