【问题标题】:Excluding specific items in a collection when serializing to JSON序列化为 JSON 时排除集合中的特定项目
【发布时间】:2014-01-06 17:32:03
【问题描述】:

我正在尝试“挑选”我想要序列化的特定类型集合中的哪些对象。

示例设置:

public class Person
{
    public string Name { get; set; }

    public List<Course> Courses { get; set; }
}

public class Course
{
    ...

    public bool ShouldSerialize { get; set; }
}

我需要能够排除 Person.Courses 集合中 ShouldSerialize 为 false 的所有课程。这需要在 ContractResolver 中完成——ShouldSerialize 属性就是一个例子,在我的真实场景中可能还有其他标准。我宁愿不必创建 ShouldSerializeCourse (如此处指定:http://james.newtonking.com/json/help/index.html?topic=html/ConditionalProperties.htm

我似乎无法找出在 ContractResolver 中要覆盖的方法。我该怎么办?

【问题讨论】:

    标签: c# .net json json.net


    【解决方案1】:

    我认为您不能使用 ContractResolver 过滤列表,但您可以使用自定义 JsonConverter 来过滤。这是一个例子:

    class Program
    {
        static void Main(string[] args)
        {
            List<Person> people = new List<Person>
            {
                new Person 
                {
                    Name = "John",
                    Courses = new List<Course>
                    {
                        new Course { Name = "Trigonometry", ShouldSerialize = true },
                        new Course { Name = "History", ShouldSerialize = true },
                        new Course { Name = "Underwater Basket Weaving", ShouldSerialize = false },
                    }
                },
                new Person
                {
                    Name = "Georgia",
                    Courses = new List<Course>
                    {
                        new Course { Name = "Spanish", ShouldSerialize = true },
                        new Course { Name = "Pole Dancing", ShouldSerialize = false },
                        new Course { Name = "Geography", ShouldSerialize = true },
                    }
                }
            };
    
            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.Converters.Add(new CourseListConverter());
            settings.Formatting = Formatting.Indented;
    
            string json = JsonConvert.SerializeObject(people, settings);
            Console.WriteLine(json);
        }
    }
    
    class CourseListConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(List<Course>));
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, ((List<Course>)value).Where(c => c.ShouldSerialize).ToArray());
        }
    
        public override bool CanRead
        {
            get { return false; }
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    
    public class Person
    {
        public string Name { get; set; }
        public List<Course> Courses { get; set; }
    }
    
    public class Course
    {
        public string Name { get; set; }
        [JsonIgnore]
        public bool ShouldSerialize { get; set; }
    }
    

    输出:

    [
      {
        "Name": "John",
        "Courses": [
          {
            "Name": "Trigonometry"
          },
          {
            "Name": "History"
          }
        ]
      },
      {
        "Name": "Georgia",
        "Courses": [
          {
            "Name": "Spanish"
          },
          {
            "Name": "Geography"
          }
        ]
      }
    ]
    

    【讨论】:

    • 谢谢!那很完美!当然,我必须根据我的情况进行修改。 :)
    • 这是一件非常微妙的事情,ToArrayWhere 子句之后通过更改集合类型来防止堆栈溢出。我从一个数组开始,ToList
    • 我使用此解决方案进行了一些可能有用的调整。我创建了一个一次性的List&lt;T&gt; 子类,称为FilteredList,然后在CanConvert() 中排除它的类型,并在传递给WriteJson() 中的Serialize() 方法时将过滤后的列表包装在FilteredList 中。具体来说,CanConvert() 的主体看起来像 typeof(FilteredList) != objectType &amp;&amp; typeof(IList&lt;MyType&gt;).IsAssignableFrom(objectType)。这样,输入可以是 List 或 Array 或任何其他 IList,但在尝试转换自己的输出时不会意外进入递归堆栈溢出。
    【解决方案2】:

    您不应该将逻辑放入序列化程序中。而是过滤集合,然后将其序列化。在这种情况下,您可以使用类似的东西

    Person.Courses = Person.Courses.Where(t=> t.ShouldSerialize == false);
    
    return Person;
    

    如果您确实需要将合约解析器添加到您的序列化程序中,您可以在 Global.asax.cs 中进行(假设您使用的是 asp.net)

    【讨论】:

    • 问题是能够从合约解析器中进行“过滤”。
    • 另外,这样做会修改源对象,这在我的情况下是不可取的。
    • 由于您使用的是 Json.Net,您唯一能做的就是创建 ShouldSerializeX ShouldSerializeY 方法以检查它是否要被序列化.. 你是什么意思“这修改源对象“?我猜它只适用于序列化。
    • 通过设置Person.Courses = ...,原始的 Person.Courses 丢失了,当然,除非我先将它保存在临时变量中......我猜这是可以接受的。哦,好吧。
    • :) 我认为在序列化之前修改源对象是一种更简单的方法。因为您可能不需要服务中的属性,而是另一个服务调用。如果你打算使用你提供的链接,你需要为它创建另一个实体,这对你来说是一个工作量:)
    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多