【问题标题】:Serializing only selected properties with Json.Net使用 Json.Net 仅序列化选定的属性
【发布时间】:2019-02-15 13:51:56
【问题描述】:

我想使用 Json.NET 仅序列化对象的某些属性。
我正在使用类似Json.net serialize only certain properties 帖子中描述的解决方案。
我的问题是我每次都想选择不同的属性,并且出于性能原因对CreateContract(又调用CreateProperties)的调用被缓存(源代码:https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs)。

有没有办法只序列化我想要的属性,每次都指定不同的属性,可能不需要重写整个DefaultContractResolver 类?

这是一个显示这个问题的程序:

    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using Newtonsoft.Json.Serialization;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    class Person {
        public int Id;
        public string FirstName;
        public string LastName;
    }

    public class SelectedPropertiesContractResolver<T> : CamelCasePropertyNamesContractResolver {

        HashSet<string> _selectedProperties;

        public SelectedPropertiesContractResolver(IEnumerable<string> selectedProperties) {
            _selectedProperties = selectedProperties.ToHashSet();
        }

        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
            if (type == typeof(T)) {
                return base.CreateProperties(type, memberSerialization)
                    .Where(p => _selectedProperties.Contains(p.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList();
            }
            return base.CreateProperties(type, memberSerialization);
        }

    }


    class Program {

        static void Main(string[] args) {

            var person = new Person { Id = 1, FirstName = "John", LastName = "Doe" };

            var serializer1 = new JsonSerializer {
                ContractResolver = new SelectedPropertiesContractResolver<Person>(new[] { "Id", "FirstName" })
            };

            // This will contain only Id and FirstName, as expected
            string json1 = JObject.FromObject(person, serializer1).ToString();

            var serializer2 = new JsonSerializer {
                ContractResolver = new SelectedPropertiesContractResolver<Person>(new[] { "LastName" })
            };

            // Since calls to CreateProperties are cached, this will contain Id and FirstName as well, instead of LastName.
            string json2 = JObject.FromObject(person, serializer2).ToString();

        }
    }

【问题讨论】:

标签: c# json json.net


【解决方案1】:

您可以覆盖 ResolveContract 方法并始终创建新合约(甚至更好 - 根据类型 T_selectedProperties 内容提供您自己的奇特缓存方式)

public class SelectedPropertiesContractResolver<T> : CamelCasePropertyNamesContractResolver {

    ...

    public override JsonContract ResolveContract(Type type)
    {
        return CreateContract(type);
    }
}

【讨论】:

  • 这很好,但它依赖于 ResolveContract 的基本实现除了调用 CreateContract 并缓存结果之外什么都不做......
  • 好吧,object.GetHashCode 的任何覆盖都依赖于它的基本实现除了获取哈希码之外什么都不做的事实。我在这里看到同样的情况,ResolveContract 方法解决了给定类型的合同,如果基础实现做了除此之外的任何事情,那么我会说基础实现有问题。
  • 但是您可以添加调用“base.ResolveContract”并忽略结果。但除非基础实现做一些特定的事情,否则它是没有意义的。
【解决方案2】:

您可以使用代码来解决您的任务:

        static void Main(string[] args)
        {
            var myObject = new {Id = 123, Name = "Test", IsTest = true};
            var propertyForSerialization = new List<string> { "Id", "Name" };

            var result = GetSerializedObject(myObject, propertyForSerialization);
        }

        private static string GetSerializedObject(object objForSerialize, List<string> propertyForSerialization)
        {
            var customObject = new ExpandoObject() as IDictionary<string, Object>;
            Type myType = objForSerialize.GetType();

            IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());

            foreach (PropertyInfo prop in props)
            {
                foreach (var propForSer in propertyForSerialization)
                {
                    if (prop.Name == propForSer)
                    {
                        customObject.Add(prop.Name, prop.GetValue(objForSerialize, null));
                    }
                }
            }

           return JsonConvert.SerializeObject(customObject);
        }

【讨论】:

    【解决方案3】:

    几个可能的解决方案,基于 cmets 和选定的答案。

    使用条件序列化:

        using Newtonsoft.Json;
        using Newtonsoft.Json.Linq;
        using Newtonsoft.Json.Serialization;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Reflection;
    
        public interface ISerializeSelectedPropertiesOnly {
            bool ShouldSerialize(string propertyName);
        }
    
        public class Person : ISerializeSelectedPropertiesOnly {
            public int Id;
            public string FirstName;
            public string LastName;
            public HashSet<string> _propertiesToSerialize;
            public bool ShouldSerialize(string propertyName) {
                return _propertiesToSerialize?.Contains(propertyName, StringComparer.OrdinalIgnoreCase) ?? true;
            }
        }
    
        public class SelectedPropertiesContractResolver : CamelCasePropertyNamesContractResolver {
            protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
                JsonProperty property = base.CreateProperty(member, memberSerialization);
                if (typeof(ISerializeSelectedPropertiesOnly).IsAssignableFrom(property.DeclaringType)) {
                    property.ShouldSerialize = instance => ((ISerializeSelectedPropertiesOnly)instance).ShouldSerialize(property.PropertyName);
                }
                return property;
            }
        }
    
        class Program {
            static void Main(string[] args) {
                var person = new Person { Id = 1, FirstName = "John", LastName = "Doe" };
                person._propertiesToSerialize = new HashSet<string> { "Id", "FirstName" };
                var serializer = new JsonSerializer {
                    ContractResolver = new SelectedPropertiesContractResolver()
                };
                string json1 = JObject.FromObject(person, serializer).ToString();
                person._propertiesToSerialize = new HashSet<string> { "LastName" };
                string json2 = JObject.FromObject(person, serializer).ToString();
            }
        }
    

    覆盖ResolveContract:

        using Newtonsoft.Json;
        using Newtonsoft.Json.Linq;
        using Newtonsoft.Json.Serialization;
        using System;
        using System.Collections.Generic;
        using System.Linq;
    
        public class Person {
            public int Id;
            public string FirstName;
            public string LastName;
        }
    
        public class SelectedPropertiesContractResolver<T> : CamelCasePropertyNamesContractResolver {
            HashSet<string> _selectedProperties;
            public SelectedPropertiesContractResolver(IEnumerable<string> selectedProperties) {
                _selectedProperties = selectedProperties.ToHashSet();
            }
            public override JsonContract ResolveContract(Type type) {
                return CreateContract(type);
            }
            protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
                if (type == typeof(T)) {
                    return base.CreateProperties(type, memberSerialization)
                        .Where(p => _selectedProperties.Contains(p.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList();
                }
                return base.CreateProperties(type, memberSerialization);
            }
        }
    
    
        class Program {
            static void Main(string[] args) {
                var person = new Person { Id = 1, FirstName = "John", LastName = "Doe" };
                var serializer = new JsonSerializer {
                    ContractResolver = new SelectedPropertiesContractResolver<Person>(new HashSet<string> { "Id", "FirstName" })
                };
                string json1 = JObject.FromObject(person, serializer).ToString();
                serializer = new JsonSerializer {
                    ContractResolver = new SelectedPropertiesContractResolver<Person>(new HashSet<string> { "LastName" })
                };
                string json2 = JObject.FromObject(person, serializer).ToString();
                Console.WriteLine(json1);
                Console.WriteLine(json2);
            }
        } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-29
      • 2013-06-12
      • 1970-01-01
      • 2013-09-02
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      相关资源
      最近更新 更多