1- using Microsoft . AspNetCore . Builder ;
1+ using System ;
2+ using System . IO ;
3+ using System . Text . Json . Nodes ; // for JsonValue
4+ using Microsoft . AspNetCore . Builder ;
25using Microsoft . AspNetCore . Http ;
3- using Microsoft . AspNetCore . Mvc ;
46using Microsoft . AspNetCore . Routing ;
57using Microsoft . Extensions . DependencyInjection ;
8+ using Newtonsoft . Json ;
9+ using Newtonsoft . Json . Linq ;
610
711namespace Infragistics . QueryBuilder . Executor
812{
913 public static class QueryExecutorExtensions
1014 {
11- public static IServiceCollection AddQueryBuilder < TMyDbContext , TResults > ( this IServiceCollection services ) where TResults : class
15+ public static IServiceCollection AddQueryBuilder < TMyDbContext , TResults > ( this IServiceCollection services )
16+ where TResults : class
1217 {
1318 services . AddScoped < QueryBuilderService < TMyDbContext , TResults > > ( ) ;
1419 return services ;
1520 }
1621
17- public static IEndpointRouteBuilder UseQueryBuilder < TMyDbContext , TResults > ( this IEndpointRouteBuilder endpoints , string path ) where TMyDbContext : class where TResults : class
22+ public static IEndpointRouteBuilder UseQueryBuilder < TMyDbContext , TResults > (
23+ this IEndpointRouteBuilder endpoints , string path )
24+ where TMyDbContext : class
25+ where TResults : class
1826 {
19- endpoints . MapPost ( path , ( [ FromBody ] Query query , QueryBuilderService < TMyDbContext , TResults > queryBuilderService ) =>
27+ endpoints . MapPost ( path , async ( HttpContext ctx , QueryBuilderService < TMyDbContext , TResults > svc ) =>
2028 {
21- if ( query != null )
29+ string json ;
30+ using ( var sr = new StreamReader ( ctx . Request . Body ) )
2231 {
23- var result = queryBuilderService . RunQuery ( query ) ;
24- return Results . Ok ( result ) ;
32+ json = await sr . ReadToEndAsync ( ) ;
2533 }
26- else
34+
35+ var settings = new Newtonsoft . Json . JsonSerializerSettings
2736 {
37+ DateParseHandling = Newtonsoft . Json . DateParseHandling . None ,
38+ ReferenceLoopHandling = Newtonsoft . Json . ReferenceLoopHandling . Ignore ,
39+ } ;
40+
41+ // 1) Convert QueryFilter by shape (tree vs leaf)
42+ settings . Converters . Add ( new QueryFilterCreationConverter ( ) ) ;
43+ // 2) Let Newtonsoft write primitives (number/string/bool) into System.Text.Json.Nodes.JsonValue
44+ settings . Converters . Add ( new NewtonsoftJsonValueConverter ( ) ) ;
45+
46+ var query = Newtonsoft . Json . JsonConvert . DeserializeObject < Query > ( json , settings ) ;
47+ if ( query is null )
2848 return Results . BadRequest ( "Wrong or missing query" ) ;
29- }
30- } ) . WithTags ( [ "QueryBuilder" ] ) . Accepts < Query > ( "application/json" ) . Produces < TResults > ( ) ;
49+
50+ var result = svc . RunQuery ( query ) ;
51+ return Results . Ok ( result ) ;
52+ } )
53+ . WithTags ( [ "QueryBuilder" ] )
54+ . Accepts < Query > ( "application/json" )
55+ . Produces < TResults > ( ) ;
56+
3157 return endpoints ;
3258 }
3359 }
34- }
60+
61+ /// <summary>
62+ /// Resolves QueryFilter to the only concrete types available:
63+ /// - FilteringExpressionsTree (has "filteringOperands")
64+ /// - FilteringExpression (leaf)
65+ /// </summary>
66+ file sealed class QueryFilterCreationConverter : Newtonsoft . Json . JsonConverter
67+ {
68+ private static readonly Type BaseType = typeof ( QueryFilter ) ;
69+ private static readonly Type TreeType = typeof ( FilteringExpressionsTree ) ;
70+ private static readonly Type LeafType = typeof ( FilteringExpression ) ;
71+
72+ public override bool CanConvert ( Type objectType ) => objectType == BaseType ;
73+
74+ public override object ReadJson (
75+ Newtonsoft . Json . JsonReader reader ,
76+ Type objectType ,
77+ object ? existingValue ,
78+ Newtonsoft . Json . JsonSerializer serializer )
79+ {
80+ var jo = JObject . Load ( reader ) ;
81+
82+ // Tree if it has an array "filteringOperands"; otherwise leaf
83+ var isTree = jo [ "filteringOperands" ] is JArray ;
84+ var target = isTree ? TreeType : LeafType ;
85+
86+ return jo . ToObject ( target , serializer ) ! ;
87+ }
88+
89+ public override void WriteJson (
90+ Newtonsoft . Json . JsonWriter writer ,
91+ object ? value ,
92+ Newtonsoft . Json . JsonSerializer serializer )
93+ {
94+ serializer . Serialize ( writer , value ) ;
95+ }
96+ }
97+
98+ /// <summary>
99+ /// Newtonsoft -> System.Text.Json.Nodes.JsonValue bridge.
100+ /// Handles primitives and null (which is all your payload needs for "searchVal": 10253).
101+ /// </summary>
102+ file sealed class NewtonsoftJsonValueConverter : Newtonsoft . Json . JsonConverter
103+ {
104+ public override bool CanConvert ( Type objectType )
105+ => objectType == typeof ( JsonValue ) ;
106+
107+ public override object ? ReadJson (
108+ Newtonsoft . Json . JsonReader reader ,
109+ Type objectType ,
110+ object ? existingValue ,
111+ Newtonsoft . Json . JsonSerializer serializer )
112+ {
113+ if ( reader . TokenType == Newtonsoft . Json . JsonToken . Null )
114+ return null ;
115+
116+ // Extract the raw .NET value from Newtonsoft token
117+ var token = JToken . ReadFrom ( reader ) ;
118+
119+ // For numbers/bools/strings, token.ToObject<object>() is a primitive -> wrap it
120+ // For safety, we only support primitives here since JsonValue is a primitive wrapper.
121+ // (Your payload uses numbers/strings/null for searchVal.)
122+ var primitive = token . Type switch
123+ {
124+ JTokenType . Integer => ( object ) token . ToObject < long > ( ) ! ,
125+ JTokenType . Float => token . ToObject < double > ( ) ! ,
126+ JTokenType . Boolean => token . ToObject < bool > ( ) ! ,
127+ JTokenType . String => token . ToObject < string > ( ) ! ,
128+ JTokenType . Null => null ! ,
129+ _ => throw new NotSupportedException (
130+ $ "JsonValue converter only supports primitives. Got { token . Type } .")
131+ } ;
132+
133+ return primitive is null ? null : JsonValue . Create ( primitive ) ;
134+ }
135+
136+ public override void WriteJson (
137+ Newtonsoft . Json . JsonWriter writer ,
138+ object ? value ,
139+ Newtonsoft . Json . JsonSerializer serializer )
140+ {
141+ if ( value is null )
142+ {
143+ writer . WriteNull ( ) ;
144+ return ;
145+ }
146+
147+ var jv = ( JsonValue ) value ;
148+ // Extract the underlying primitive and write it as a native JSON value
149+ if ( jv . TryGetValue ( out long l ) ) { writer . WriteValue ( l ) ; return ; }
150+ if ( jv . TryGetValue ( out double d ) ) { writer . WriteValue ( d ) ; return ; }
151+ if ( jv . TryGetValue ( out bool b ) ) { writer . WriteValue ( b ) ; return ; }
152+ if ( jv . TryGetValue ( out string s ) ) { writer . WriteValue ( s ) ; return ; }
153+
154+ // Fallback: write as string representation
155+ writer . WriteValue ( jv . ToJsonString ( ) ) ;
156+ }
157+ }
158+ }
0 commit comments