using System;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Luticate2.Auth.Utils.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}";
}
}
}