|
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
using System.Linq.Expressions;
|
|
|
using System.Text.Json;
|
|
|
+using System.Text.Json.Serialization;
|
|
|
using System.Text.RegularExpressions;
|
|
|
using static InABox.Core.IFilter;
|
|
|
|
|
@@ -55,6 +56,11 @@ namespace InABox.Core
|
|
|
SerializedSubquery = subquery;
|
|
|
}
|
|
|
|
|
|
+ [JsonConstructor]
|
|
|
+ private SubQuerySerializationWrapper()
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
public string Type { get; set; }
|
|
|
public string SerializedSubquery { get; set; }
|
|
|
}
|
|
@@ -279,7 +285,7 @@ namespace InABox.Core
|
|
|
/// <summary>
|
|
|
/// List of filters to apply to the main filter with boolean operator AND. The <see cref="Ands"/> take precedence over the <see cref="Ors"/>.
|
|
|
/// </summary>
|
|
|
- IList<IFilter> Ands { get; }
|
|
|
+ IEnumerable<IFilter> Ands { get; }
|
|
|
/// <summary>
|
|
|
/// List of filters to apply to the main filter with boolean operator OR. The <see cref="Ands"/> take precedence over the <see cref="Ors"/>.
|
|
|
/// </summary>
|
|
@@ -494,7 +500,7 @@ namespace InABox.Core
|
|
|
|
|
|
public bool IsNot { get; set; }
|
|
|
|
|
|
- IList<IFilter> IFilter.Ands => Ands.OfType<IFilter>().ToList();
|
|
|
+ IEnumerable<IFilter> IFilter.Ands => Ands;
|
|
|
|
|
|
IEnumerable<IFilter> IFilter.Ors => Ors;
|
|
|
|
|
@@ -2120,14 +2126,18 @@ namespace InABox.Core
|
|
|
|
|
|
public override IFilter? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
|
{
|
|
|
-
|
|
|
if (reader.TokenType == JsonTokenType.Null)
|
|
|
return null;
|
|
|
-
|
|
|
- IFilter? _result = null;
|
|
|
- Type? _type;
|
|
|
-
|
|
|
- while (reader.TokenType != JsonTokenType.EndObject && reader.Read())
|
|
|
+
|
|
|
+ Type? _type = null;
|
|
|
+ string? propertyName = null;
|
|
|
+ Operator op = default;
|
|
|
+ bool isNot = false;
|
|
|
+ object? value = null;
|
|
|
+ List<IFilter> ands = new List<IFilter>();
|
|
|
+ List<IFilter> ors = new List<IFilter>();
|
|
|
+
|
|
|
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
|
|
|
{
|
|
|
var tag = reader.GetString();
|
|
|
reader.Read();
|
|
@@ -2137,111 +2147,105 @@ namespace InABox.Core
|
|
|
if (!string.IsNullOrWhiteSpace(sType))
|
|
|
{
|
|
|
_type = Type.GetType(sType);
|
|
|
- if (_type != null)
|
|
|
- {
|
|
|
- var gType = typeof(Filter<>).MakeGenericType(_type);
|
|
|
- _result = Activator.CreateInstance(gType) as IFilter;
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
- else if (string.Equals(tag, "Expression") && _result != null)
|
|
|
- _result.Property = reader.GetString() ?? "";
|
|
|
- else if (string.Equals(tag, "Operator") && _result != null)
|
|
|
- _result.Operator = Enum.Parse<Operator>(reader.GetString());
|
|
|
- else if (string.Equals(tag, "IsNot") && _result != null)
|
|
|
- _result.IsNot = reader.GetBoolean();
|
|
|
- else if (string.Equals(tag, "FilterConstant") && _result != null)
|
|
|
- _result.Value = Enum.Parse<FilterConstant>(reader.GetString());
|
|
|
- else if (string.Equals(tag, "CustomValue") && _result != null)
|
|
|
- _result.Value = new CustomFilterValue(Convert.FromBase64String(reader.GetString()));
|
|
|
- else if (string.Equals(tag, "Value") && _result != null)
|
|
|
+ else if (string.Equals(tag, "Expression"))
|
|
|
+ propertyName = reader.GetString() ?? "";
|
|
|
+ else if (string.Equals(tag, "Operator"))
|
|
|
+ op = Enum.Parse<Operator>(reader.GetString());
|
|
|
+ else if (string.Equals(tag, "IsNot"))
|
|
|
+ isNot = reader.GetBoolean();
|
|
|
+ else if (string.Equals(tag, "FilterConstant"))
|
|
|
+ value = Enum.Parse<FilterConstant>(reader.GetString());
|
|
|
+ else if (string.Equals(tag, "CustomValue"))
|
|
|
+ value = new CustomFilterValue(Convert.FromBase64String(reader.GetString()));
|
|
|
+ else if (string.Equals(tag, "Value"))
|
|
|
{
|
|
|
- if (reader.TokenType == JsonTokenType.StartArray)
|
|
|
- {
|
|
|
- List<object> values = new List<object>();
|
|
|
- while (reader.TokenType != JsonTokenType.EndArray)
|
|
|
- {
|
|
|
- reader.Read();
|
|
|
- var value = ReadJson(reader);
|
|
|
- values.Add(value);
|
|
|
- }
|
|
|
-
|
|
|
- _result.Value = values.ToArray();
|
|
|
- }
|
|
|
- else
|
|
|
- _result.Value = ReadJson(reader);
|
|
|
-
|
|
|
+ value = ReadJson(ref reader);
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
- else if (string.Equals(tag, "Ands") && _result != null)
|
|
|
+ else if (string.Equals(tag, "Ands"))
|
|
|
{
|
|
|
- reader.Read();
|
|
|
- while (reader.TokenType != JsonTokenType.EndObject)
|
|
|
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
|
|
{
|
|
|
var and = Read(ref reader, typeof(IFilter), options);
|
|
|
if (and != null)
|
|
|
- _result.Ands.Add(and);
|
|
|
+ ands.Add(and);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- else if (string.Equals(tag, "Ors") && _result != null)
|
|
|
+ else if (string.Equals(tag, "Ors"))
|
|
|
{
|
|
|
- reader.Read();
|
|
|
- while (reader.TokenType != JsonTokenType.EndObject)
|
|
|
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
|
|
{
|
|
|
- var or = Read(ref reader, typeof(ISortOrder), options);
|
|
|
+ var or = Read(ref reader, typeof(IFilter), options);
|
|
|
if (or != null)
|
|
|
- _result.Ands.Add(or);
|
|
|
+ ors.Add(or);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
- return _result;
|
|
|
|
|
|
+ if (_type is null)
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var result = (Activator.CreateInstance(_type) as IFilter)!;
|
|
|
+
|
|
|
+ result.Property = propertyName ?? "";
|
|
|
+ result.Operator = op;
|
|
|
+ result.IsNot = isNot;
|
|
|
+ if(op == Operator.InQuery || op == Operator.NotInQuery)
|
|
|
+ {
|
|
|
+ var wrapper = Serialization.Deserialize<SubQuerySerializationWrapper>(value as string);
|
|
|
+ if(wrapper != null)
|
|
|
+ {
|
|
|
+ result.Value = Serialization.Deserialize(typeof(SubQuery<>).MakeGenericType(CoreUtils.GetEntity(wrapper.Type)), wrapper.SerializedSubquery);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(op == Operator.InList || op == Operator.NotInList)
|
|
|
+ {
|
|
|
+ result.Value = CoreUtils.ChangeType(value, result.Type.MakeArrayType());
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ result.Value = CoreUtils.ChangeType(value, result.Type);
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach(var or in ors)
|
|
|
+ {
|
|
|
+ result.Or(or);
|
|
|
+ }
|
|
|
+ foreach(var and in ands)
|
|
|
+ {
|
|
|
+ result.And(and);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
public override void Write(Utf8JsonWriter writer, IFilter filter, JsonSerializerOptions options)
|
|
|
{
|
|
|
-
|
|
|
var prop = filter.Property;
|
|
|
|
|
|
var op = filter.Operator;
|
|
|
var val = filter.Value;
|
|
|
|
|
|
- var ands = filter.Ands.ToList();
|
|
|
- var ors = filter.Ors.ToList();
|
|
|
-
|
|
|
- //var table = value as DataTable;
|
|
|
- //// Save Columns
|
|
|
writer.WriteStartObject();
|
|
|
|
|
|
- writer.WriteString("$type",filter.GetType().FullName);
|
|
|
-
|
|
|
- writer.WriteString("Expression",prop);
|
|
|
-
|
|
|
- writer.WriteString("Operator",op.ToString());
|
|
|
-
|
|
|
- writer.WriteBoolean("IsNot",filter.IsNot);
|
|
|
+ // For the above reader to work, the $type property *must* be serialised first.
|
|
|
+ writer.WriteString("$type", filter.GetType().FullName);
|
|
|
+ writer.WriteString("Expression", prop);
|
|
|
+ writer.WriteString("Operator", op.ToString());
|
|
|
+ writer.WriteBoolean("IsNot", filter.IsNot);
|
|
|
|
|
|
if (val is FilterConstant fcVal)
|
|
|
- writer.WriteString("FilterConstant",fcVal.ToString());
|
|
|
+ writer.WriteString("FilterConstant", fcVal.ToString());
|
|
|
else if(val is CustomFilterValue cVal)
|
|
|
- writer.WriteBase64String("CustomValue",cVal.Data);
|
|
|
+ writer.WriteBase64String("CustomValue", cVal.Data);
|
|
|
else
|
|
|
{
|
|
|
writer.WritePropertyName("Value");
|
|
|
var valType = val == null ? filter.Type : val.GetType();
|
|
|
- if (valType.IsArray)
|
|
|
- {
|
|
|
- writer.WriteStartArray();
|
|
|
- var arr = (val as IEnumerable)!;
|
|
|
- foreach (var v in arr)
|
|
|
- WriteJson(writer, v);
|
|
|
- writer.WriteEndArray();
|
|
|
- }
|
|
|
- else if (valType.IsConstructedGenericType && valType.GetGenericTypeDefinition() == typeof(SubQuery<>))
|
|
|
+ if (valType.IsConstructedGenericType && valType.GetGenericTypeDefinition() == typeof(SubQuery<>))
|
|
|
{
|
|
|
var serialized = Serialization.Serialize(val);
|
|
|
var wrapper = new SubQuerySerializationWrapper(valType.GenericTypeArguments[0].EntityName(), serialized);
|
|
@@ -2251,24 +2255,25 @@ namespace InABox.Core
|
|
|
writer.WriteStringValue(str);
|
|
|
}
|
|
|
else
|
|
|
- WriteJson(writer,val);
|
|
|
-
|
|
|
+ {
|
|
|
+ WriteJson(writer, val);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (ands?.Any() == true)
|
|
|
+ if (filter.Ands.Any())
|
|
|
{
|
|
|
writer.WritePropertyName("Ands");
|
|
|
writer.WriteStartArray();
|
|
|
- foreach (var and in ands)
|
|
|
+ foreach (var and in filter.Ands)
|
|
|
Write(writer, and, options);
|
|
|
writer.WriteEndArray();
|
|
|
}
|
|
|
|
|
|
- if (ors?.Any() == true)
|
|
|
+ if (filter.Ors.Any())
|
|
|
{
|
|
|
writer.WritePropertyName("Ors");
|
|
|
writer.WriteStartArray();
|
|
|
- foreach (var or in ors)
|
|
|
+ foreach (var or in filter.Ors)
|
|
|
Write(writer, or, options);
|
|
|
writer.WriteEndArray();
|
|
|
}
|