using System; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Luticate2.Auth.Business.Serializers.PartialJson { /// /// Provides a method for serializing objects to JSON. /// /// This type supports the infrastructure and is not intended to be used directly /// from your code. public static class LuJsonSerializerExtensions { /// /// Serializes the specified and writes the JSON structure /// using the specified . /// /// The used to serialize the specified . /// The used to write the JSON structure. /// The to serialize. /// A that is called for every field in the /// to serialize, indicating whether the field should be serialized. public static void Serialize(this JsonSerializer jsonSerializer, JsonWriter jsonWriter, object value, Func shouldSerialize) { if (value == null) { jsonSerializer.Serialize(jsonWriter, value); } else { var context = new LuSerializerContext(shouldSerialize); var token = JToken.FromObject(value, jsonSerializer); if (token is JArray array) { RemoveArrayElements(array, null, context); array.WriteTo(jsonWriter); } else { if (token is JObject @object) { RemoveObjectProperties(@object, null, context); @object.WriteTo(jsonWriter); } else { token.WriteTo(jsonWriter); } } } } private static void RemoveArrayElements(JArray array, string currentPath, LuSerializerContext context) { var containsChildItems = array.Count > 0; array.OfType() .ToList() .ForEach(childObject => RemoveObjectProperties(childObject, currentPath, context)); // PartialResponse should only remove an array when it initially contained items, but was empty after filtering. if (containsChildItems) { RemoveArrayIfEmpty(array); } } private static void RemoveArrayIfEmpty(JArray array) { if (array.Count == 0) { if (array.Parent is JProperty && array.Parent.Parent != null) { array.Parent.Remove(); } else if (array.Parent is JArray) { array.Remove(); } } } private static void RemoveObjectProperties(JObject @object, string currentPath, LuSerializerContext context) { @object.Properties() .Where(property => { var path = CombinePath(currentPath, property.Name); return !context.ShouldSerialize(path); }) .ToList() .ForEach(property => property.Remove()); @object.Properties() .Where(property => property.Value is JObject) .ToList() .ForEach(property => { var path = CombinePath(currentPath, property.Name); RemoveObjectProperties((JObject)property.Value, path, context); }); @object.Properties() .Where(property => property.Value is JArray) .ToList() .ForEach(property => { var path = CombinePath(currentPath, property.Name); RemoveArrayElements((JArray)property.Value, path, context); }); RemoveObjectIfEmpty(@object); } private static void RemoveObjectIfEmpty(JObject @object) { if (!@object.Properties().Any()) { if (@object.Parent is JProperty && @object.Parent.Parent != null) { @object.Parent.Remove(); } else if (@object.Parent is JArray) { @object.Remove(); } } } private static string CombinePath(string path, string name) { if (string.IsNullOrEmpty(path)) { return name; } return $"{path}/{name}"; } } }