【问题标题】:Mapping multiple property names to the same field in Newtonsoft.JSON将多个属性名称映射到 Newtonsoft.JSON 中的同一字段
【发布时间】:2018-03-13 10:10:36
【问题描述】:

我在分布式系统中有两个组件,它们发送使用 Newtonsoft.JSON (JSON.Net) 进行序列化/反序列化的消息。

消息属性目前以挪威语发送,我希望将代码库翻译成英语。由于某些消息会以挪威语发送,并由已升级到英文版的组件处理,因此需要能够同时支持这两种语言。

我希望在反序列化时,“挪威”属性名称和英语都将映射到同一个属性。例如:

例如,取英语中的“name”或挪威语中的“navn”。

public class Message
{
     [JsonProperty("Navn")]
     public string Name { get; set;}
}

上面的问题是它只能从Navn => Name 映射。我希望它将NavnName 映射到Name

这在 Newtonsoft.JSON 中是否可用,无需太多自定义编码?

【问题讨论】:

    标签: json json.net


    【解决方案1】:

    您可以在此答案中使用自定义 ContractResolver:

    Json.NET deserialize or serialize json string and map properties to different property names defined at runtime

    或者

    使用 [JsonProperty("")] 查找属性名称的不同变体并返回如下属性之一:

    public class Message
    {
       private string _name;
    
       [JsonProperty("Navn" )]
       public string NorwegianName { get; set; }
    
       [JsonProperty("Name")]
       public string Name { 
          get { return _name ?? NorwegianName; } 
          set { _name = value; } }
    }
    

    这将返回带有 JSON 属性名称的名称:NavnName

    【讨论】:

    • 虽然这是一个选项,但我有大量的类和属性名称,因此当我有数百个属性时,只为 1 个属性执行所有这些代码会非常麻烦。我更喜欢更简洁的方式,比如为同一个属性指定多个 [JsonProperty] 属性。
    • 那么另一个选项是在链接的答案中使用自定义 ContractResolver 。它基本上使用字典将属性映射到其他名称。我看不到将多个 [JsonProperty] 属性分配给一个属性的方法。我将尝试另一件可能比这更干净的事情并尽快更新答案。
    • @KarlCassar 更新了答案。这使用了多个 [JsonProperty] 并消除了附加属性的使用。如果您使用 Name 调用,您将获得带有属性名称 Navn 或 Name 的结果。但是,考虑到您有很多这些,最好使用自定义 ContractResolver 并映射到其他属性名称。
    • 完美,非常适合我的需要。我不喜欢 ContractResolver 最终将映射放在不同位置的事实。我喜欢他们接近消息来源,所以很明显正在发生什么。
    【解决方案2】:

    我需要这样做,经过一些实验后,我发现为同一个 MemberInfo 返回多个 JsonProperties 的解析器运行良好。它还使用单个指定名称进行序列化,但可以从任何一个名称中读取。我正在使用此迁移名称,它不希望在源 json 中找到多个可能的名称。

    public class TestClass
    {
        [FallbackJsonProperty("fn", "firstName", "first_name")]
        public string FirstName {get;set;}
    }
    
    public class ThingResolver : CamelCasePropertyNamesContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var typeMembers = GetSerializableMembers(type);
            var properties = new List<JsonProperty>();
    
            foreach (var member in typeMembers)
            {
                var property = CreateProperty(member, memberSerialization);
                properties.Add(property);
                
                var fallbackAttribute = member.GetCustomAttribute<FallbackJsonProperty>();
    
                if (fallbackAttribute == null)
                {
                    continue;
                }
    
                property.PropertyName = fallbackAttribute.PreferredName;
                
                foreach (var alternateName in fallbackAttribute.FallbackReadNames)
                {
                    var fallbackProperty = CreateProperty(member, memberSerialization);
                    fallbackProperty.PropertyName = alternateName;
                    fallbackProperty.ShouldSerialize = (x) => false;
                    properties.Add(fallbackProperty);
                }
            }
    
            return properties;
        }
    }
    
    [AttributeUsage(AttributeTargets.Property)]
    public class FallbackJsonProperty : Attribute
    {
        public string PreferredName { get; }
        public string[] FallbackReadNames { get; }
    
        public FallbackJsonProperty(string preferredName, params string[] fallbackReadNames)
        {
            PreferredName = preferredName;
            FallbackReadNames = fallbackReadNames;
        }
    }
    

    【讨论】:

    • 这在 JsonSerializerSettings 配置为 DefaultValueHandling = DefaultValueHandling.Ignore 时效果很好但是,请注意,将其设置为 DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate 可能会导致您的某些值在处理其中一个备用名称时被默认覆盖:如果您没有预料到,这可能会导致调试非常混乱。
    猜你喜欢
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 2012-04-02
    相关资源
    最近更新 更多