这里的基本困难是Json.NET是基于契约的序列化器,它为要序列化的每种 类型
创建契约,然后根据契约对序列进行(反)序列化。如果类型出现在对象层次结构的多个位置,则适用相同的合同。但是您要根据给定类型在层次结构中的位置有选择地包括属性,这与基本的“一种类型一个合同”设计冲突。
解决此问题的一种快速方法是将序列化为
JObject,然后
JToken.SelectTokens()仅选择要返回的JSON数据,然后删除其他所有内容。由于
SelectTokens完全支持JSONPath查询语法,因此您可以有选择地包括使用数组和属性通配符或其他过滤器,例如:
"$.FirstLevel[*].Bar"
包括
"Bar"在以
"FirstLevel"根对象命名的属性的所有数组成员中命名的所有属性。
这样可以减少所需的网络使用量,但不会节省服务器上的任何处理时间。
可以通过以下扩展方法完成删除:
public static partial class JsonExtensions{ public static TJToken RemoveAllExcept<TJToken>(this TJToken obj, IEnumerable<string> paths) where TJToken : JToken { if (obj == null || paths == null) throw new NullReferenceException(); var keepers = new HashSet<JToken>(paths.SelectMany(path => obj.SelectTokens(path)), ObjectReferenceEqualityComparer<JToken>.Default); var keepersAndParents = new HashSet<JToken>(keepers.SelectMany(t => t.AncestorsAndSelf()), ObjectReferenceEqualityComparer<JToken>.Default); // Keep any token that is a keeper, or a child of a keeper, or a parent of a keeper // I.e. if you have a path ""$.A.B" and it turns out that B is an object, then everything // under B should be kept. foreach (var token in obj.DescendantsAndSelfReversed().Where(t => !keepersAndParents.Contains(t) && !t.AncestorsAndSelf().Any(p => keepers.Contains(p)))) token.RemoveFromLowestPossibleParent(); // Return the object itself for fluent style programming. return obj; } public static string SerializeAndSelectTokens<T>(T root, string[] paths, Formatting formatting = Formatting.None, JsonSerializerSettings settings = null) { var obj = JObject.FromObject(root, JsonSerializer.CreateDefault(settings)); obj.RemoveAllExcept(paths); var json = obj.ToString(formatting); return json; } public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken { if (node == null) return null; JToken toRemove; var property = node.Parent as JProperty; if (property != null) { // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should toRemove = property; property.Value = null; } else { toRemove = node; } if (toRemove.Parent != null) toRemove.Remove(); return node; } public static IEnumerable<JToken> DescendantsAndSelfReversed(this JToken node) { if (node == null) throw new ArgumentNullException(); return RecursiveEnumerableExtensions.Traverse(node, t => ListReversed(t as JContainer)); } // Iterate backwards through a list without throwing an exception if the list is modified. static IEnumerable<T> ListReversed<T>(this IList<T> list) { if (list == null) yield break; for (int i = list.Count - 1; i >= 0; i--) yield return list[i]; }}public static partial class RecursiveEnumerableExtensions{ // Rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert // to "Efficient graph traversal with LINQ - eliminating recursion" http://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion // to ensure items are returned in the order they are encountered. public static IEnumerable<T> Traverse<T>( T root, Func<T, IEnumerable<T>> children) { yield return root; var stack = new Stack<IEnumerator<T>>(); try { stack.Push((children(root) ?? Enumerable.Empty<T>()).GetEnumerator()); while (stack.Count != 0) { var enumerator = stack.Peek(); if (!enumerator.MoveNext()) { stack.Pop(); enumerator.Dispose(); } else { yield return enumerator.Current; stack.Push((children(enumerator.Current) ?? Enumerable.Empty<T>()).GetEnumerator()); } } } finally { foreach (var enumerator in stack) enumerator.Dispose(); } }}/// <summary>/// A generic object comparerer that would only use object's reference, /// ignoring any <see cref="IEquatable{T}"/> or <see cref="object.Equals(object)"/> overrides./// </summary>public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class{ // Adapted from this answer https://stackoverflow.com/a/1890230 // to https://stackoverflow.com/questions/1890058/iequalitycomparert-that-uses-referenceequals // By https://stackoverflow.com/users/177275/yurik private static readonly IEqualityComparer<T> _defaultComparer; static ObjectReferenceEqualityComparer() { _defaultComparer = new ObjectReferenceEqualityComparer<T>(); } public static IEqualityComparer<T> Default { get { return _defaultComparer; } } #region IEqualityComparer<T> Members public bool Equals(T x, T y) { return ReferenceEquals(x, y); } public int GetHashCode(T obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } #endregion}
然后像这样使用它们:
public class TestClass{ public static void Test() { var root = new RootObject { FirstLevel1 = new FirstLevel { SecondLevel1 = new List<SecondLevel> { new SecondLevel { A = "a11", B = "b11", Third1 = new ThirdLevel { Foo = "Foos11", Bar = "Bars11" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList11", Bar = "BarList11" } } } }, SecondLevel2 = new List<SecondLevel> { new SecondLevel { A = "a12", B = "b12", Third1 = new ThirdLevel { Foo = "Foos12", Bar = "Bars12" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList12", Bar = "BarList12" } } } }, }, FirstLevel2 = new FirstLevel { SecondLevel1 = new List<SecondLevel> { new SecondLevel { A = "a21", B = "b21", Third1 = new ThirdLevel { Foo = "Foos21", Bar = "Bars21" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList21", Bar = "BarList21" } } } }, SecondLevel2 = new List<SecondLevel> { new SecondLevel { A = "a22", B = "b22", Third1 = new ThirdLevel { Foo = "Foos22", Bar = "Bars22" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList22", Bar = "BarList22" } } } }, } }; Assert.IsTrue(JObject.FromObject(root).DescendantsAndSelf().OfType<JValue>().Count() == 24); // No assert var paths1 = new string[] { "$.FirstLevel2.SecondLevel1[*].A", "$.FirstLevel1.SecondLevel2[*].Third2[*].Bar", }; Test(root, paths1, 2); var paths3 = new string[] { "$.FirstLevel1.SecondLevel2[*].Third2[*].Bar", }; Test(root, paths3, 1); var paths4 = new string[] { "$.*.SecondLevel2[*].Third2[*].Bar", }; Test(root, paths4, 2); } static void Test<T>(T root, string [] paths, int expectedCount) { var json = JsonExtensions.SerializeAndSelectTokens(root, paths, Formatting.Indented); Console.WriteLine("Result using paths: {0}", JsonConvert.SerializeObject(paths)); Console.WriteLine(json); Assert.IsTrue(JObject.Parse(json).DescendantsAndSelf().OfType<JValue>().Count() == expectedCount); // No assert }}public class ThirdLevel{ public string Foo { get; set; } public string Bar { get; set; }}public class SecondLevel{ public ThirdLevel Third1 { get; set; } public List<ThirdLevel> Third2 { get; set; } public string A { get; set; } public string B { get; set; }}public class FirstLevel{ public List<SecondLevel> SecondLevel1 { get; set; } public List<SecondLevel> SecondLevel2 { get; set; }}public class RootObject{ public FirstLevel FirstLevel1 { get; set; } public FirstLevel FirstLevel2 { get; set; }}
请注意,有一个增强功能请求:ADD
JsonProperty.ShouldSerialize(对象目标,字符串路径)#1857,可以更轻松地启用这种功能。
演示在这里和这里摆弄。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)