You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LuJsonSerializerExtensions.cs 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. using System;
  2. using System.Linq;
  3. using Newtonsoft.Json;
  4. using Newtonsoft.Json.Linq;
  5. namespace Luticate2.Auth.Business.Serializers.PartialJson
  6. {
  7. /// <summary>
  8. /// Provides a method for serializing objects to JSON.
  9. /// </summary>
  10. /// <remarks>This type supports the <see cref="PartialResponse.Core.Fields"/> infrastructure and is not intended to be used directly
  11. /// from your code.</remarks>
  12. public static class LuJsonSerializerExtensions
  13. {
  14. /// <summary>
  15. /// Serializes the specified <see cref="object"/> and writes the JSON structure
  16. /// using the specified <see cref="JsonWriter"/>.
  17. /// </summary>
  18. /// <param name="jsonSerializer">The <see cref="JsonSerializer"/> used to serialize the specified <see cref="object"/>.</param>
  19. /// <param name="jsonWriter">The <see cref="JsonWriter"/> used to write the JSON structure.</param>
  20. /// <param name="value">The <see cref="object"/> to serialize.</param>
  21. /// <param name="shouldSerialize">A <see cref="Func{T, TResult}"/> that is called for every field in the
  22. /// <see cref="object"/> to serialize, indicating whether the field should be serialized.</param>
  23. public static void Serialize(this JsonSerializer jsonSerializer, JsonWriter jsonWriter, object value, Func<string, bool> shouldSerialize)
  24. {
  25. if (value == null)
  26. {
  27. jsonSerializer.Serialize(jsonWriter, value);
  28. }
  29. else
  30. {
  31. var context = new LuSerializerContext(shouldSerialize);
  32. var token = JToken.FromObject(value, jsonSerializer);
  33. if (token is JArray array)
  34. {
  35. RemoveArrayElements(array, null, context);
  36. array.WriteTo(jsonWriter);
  37. }
  38. else
  39. {
  40. if (token is JObject @object)
  41. {
  42. RemoveObjectProperties(@object, null, context);
  43. @object.WriteTo(jsonWriter);
  44. }
  45. else
  46. {
  47. token.WriteTo(jsonWriter);
  48. }
  49. }
  50. }
  51. }
  52. private static void RemoveArrayElements(JArray array, string currentPath, LuSerializerContext context)
  53. {
  54. var containsChildItems = array.Count > 0;
  55. array.OfType<JObject>()
  56. .ToList()
  57. .ForEach(childObject => RemoveObjectProperties(childObject, currentPath, context));
  58. // PartialResponse should only remove an array when it initially contained items, but was empty after filtering.
  59. if (containsChildItems)
  60. {
  61. RemoveArrayIfEmpty(array);
  62. }
  63. }
  64. private static void RemoveArrayIfEmpty(JArray array)
  65. {
  66. if (array.Count == 0)
  67. {
  68. if (array.Parent is JProperty && array.Parent.Parent != null)
  69. {
  70. array.Parent.Remove();
  71. }
  72. else if (array.Parent is JArray)
  73. {
  74. array.Remove();
  75. }
  76. }
  77. }
  78. private static void RemoveObjectProperties(JObject @object, string currentPath, LuSerializerContext context)
  79. {
  80. @object.Properties()
  81. .Where(property =>
  82. {
  83. var path = CombinePath(currentPath, property.Name);
  84. return !context.ShouldSerialize(path);
  85. })
  86. .ToList()
  87. .ForEach(property => property.Remove());
  88. @object.Properties()
  89. .Where(property => property.Value is JObject)
  90. .ToList()
  91. .ForEach(property =>
  92. {
  93. var path = CombinePath(currentPath, property.Name);
  94. RemoveObjectProperties((JObject)property.Value, path, context);
  95. });
  96. @object.Properties()
  97. .Where(property => property.Value is JArray)
  98. .ToList()
  99. .ForEach(property =>
  100. {
  101. var path = CombinePath(currentPath, property.Name);
  102. RemoveArrayElements((JArray)property.Value, path, context);
  103. });
  104. RemoveObjectIfEmpty(@object);
  105. }
  106. private static void RemoveObjectIfEmpty(JObject @object)
  107. {
  108. if (!@object.Properties().Any())
  109. {
  110. if (@object.Parent is JProperty && @object.Parent.Parent != null)
  111. {
  112. @object.Parent.Remove();
  113. }
  114. else if (@object.Parent is JArray)
  115. {
  116. @object.Remove();
  117. }
  118. }
  119. }
  120. private static string CombinePath(string path, string name)
  121. {
  122. if (string.IsNullOrEmpty(path))
  123. {
  124. return name;
  125. }
  126. return $"{path}/{name}";
  127. }
  128. }
  129. }