1
- using Newtonsoft . Json ;
2
- using Newtonsoft . Json . Converters ;
3
- using Newtonsoft . Json . Linq ;
4
- using Newtonsoft . Json . Serialization ;
5
1
using System ;
6
2
using System . Collections . Generic ;
7
3
using System . IO ;
8
4
using System . Linq ;
9
5
using System . Net . Http ;
10
6
using System . Net . Http . Headers ;
11
7
using System . Text ;
8
+ using System . Text . Json ;
9
+ using System . Text . Json . Serialization ;
12
10
using System . Threading . Tasks ;
13
11
using Appwrite . Converters ;
14
12
using Appwrite . Extensions ;
@@ -29,26 +27,28 @@ public class Client
29
27
30
28
private static readonly int ChunkSize = 5 * 1024 * 1024 ;
31
29
32
- public static JsonSerializerSettings DeserializerSettings { get ; set ; } = new JsonSerializerSettings
30
+ public static JsonSerializerOptions DeserializerOptions { get ; set ; } = new JsonSerializerOptions
33
31
{
34
- MetadataPropertyHandling = MetadataPropertyHandling . Ignore ,
35
- NullValueHandling = NullValueHandling . Ignore ,
36
- ContractResolver = new CamelCasePropertyNamesContractResolver ( ) ,
37
- Converters = new List < JsonConverter >
32
+ PropertyNamingPolicy = JsonNamingPolicy . CamelCase ,
33
+ DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull ,
34
+ PropertyNameCaseInsensitive = true ,
35
+ Converters =
38
36
{
39
- new StringEnumConverter ( new CamelCaseNamingStrategy ( ) ) ,
40
- new ValueClassConverter ( )
37
+ new JsonStringEnumConverter ( JsonNamingPolicy . CamelCase ) ,
38
+ new ValueClassConverter ( ) ,
39
+ new ObjectToInferredTypesConverter ( )
41
40
}
42
41
} ;
43
42
44
- public static JsonSerializerSettings SerializerSettings { get ; set ; } = new JsonSerializerSettings
43
+ public static JsonSerializerOptions SerializerOptions { get ; set ; } = new JsonSerializerOptions
45
44
{
46
- NullValueHandling = NullValueHandling . Ignore ,
47
- ContractResolver = new CamelCasePropertyNamesContractResolver ( ) ,
48
- Converters = new List < JsonConverter >
45
+ PropertyNamingPolicy = JsonNamingPolicy . CamelCase ,
46
+ DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull ,
47
+ Converters =
49
48
{
50
- new StringEnumConverter ( new CamelCaseNamingStrategy ( ) ) ,
51
- new ValueClassConverter ( )
49
+ new JsonStringEnumConverter ( JsonNamingPolicy . CamelCase ) ,
50
+ new ValueClassConverter ( ) ,
51
+ new ObjectToInferredTypesConverter ( )
52
52
}
53
53
} ;
54
54
@@ -69,11 +69,12 @@ public Client(
69
69
_headers = new Dictionary < string , string > ( )
70
70
{
71
71
{ "content-type" , "application/json" } ,
72
- { "user-agent" , "AppwriteDotNetSDK/0.13 .0 ($ {Environment.OSVersion.Platform}; $ {Environment.OSVersion.VersionString})" } ,
72
+ { "user-agent" , $ "AppwriteDotNetSDK/0.14 .0 ({ Environment . OSVersion . Platform } ; { Environment . OSVersion . VersionString } )"} ,
73
73
{ "x-sdk-name" , ".NET" } ,
74
74
{ "x-sdk-platform" , "server" } ,
75
75
{ "x-sdk-language" , "dotnet" } ,
76
- { "x-sdk-version" , "0.13.0" } , { "X-Appwrite-Response-Format" , "1.7.0" }
76
+ { "x-sdk-version" , "0.14.0" } ,
77
+ { "X-Appwrite-Response-Format" , "1.7.0" }
77
78
} ;
78
79
79
80
_config = new Dictionary < string , string > ( ) ;
@@ -82,8 +83,6 @@ public Client(
82
83
{
83
84
SetSelfSigned ( true ) ;
84
85
}
85
-
86
- JsonConvert . DefaultSettings = ( ) => DeserializerSettings ;
87
86
}
88
87
89
88
public Client SetSelfSigned ( bool selfSigned )
@@ -189,19 +188,23 @@ private HttpRequestMessage PrepareRequest(
189
188
{
190
189
if ( parameter . Key == "file" )
191
190
{
192
- form . Add ( ( ( MultipartFormDataContent ) parameters [ "file" ] ) . First ( ) ! ) ;
191
+ var fileContent = parameters [ "file" ] as MultipartFormDataContent ;
192
+ if ( fileContent != null )
193
+ {
194
+ form . Add ( fileContent . First ( ) ! ) ;
195
+ }
193
196
}
194
197
else if ( parameter . Value is IEnumerable < object > enumerable )
195
198
{
196
199
var list = new List < object > ( enumerable ) ;
197
200
for ( int index = 0 ; index < list . Count ; index ++ )
198
201
{
199
- form . Add ( new StringContent ( list [ index ] . ToString ( ) ! ) , $ "{ parameter . Key } [{ index } ]") ;
202
+ form . Add ( new StringContent ( list [ index ] ? . ToString ( ) ?? string . Empty ) , $ "{ parameter . Key } [{ index } ]") ;
200
203
}
201
204
}
202
205
else
203
206
{
204
- form . Add ( new StringContent ( parameter . Value . ToString ( ) ! ) , parameter . Key ) ;
207
+ form . Add ( new StringContent ( parameter . Value ? . ToString ( ) ?? string . Empty ) , parameter . Key ) ;
205
208
}
206
209
}
207
210
request . Content = form ;
@@ -274,16 +277,27 @@ public async Task<String> Redirect(
274
277
}
275
278
276
279
if ( contentType . Contains ( "application/json" ) ) {
277
- message = JObject . Parse ( text ) [ "message" ] ! . ToString ( ) ;
278
- type = JObject . Parse ( text ) [ "type" ] ? . ToString ( ) ?? string . Empty ;
280
+ try
281
+ {
282
+ using var errorDoc = JsonDocument . Parse ( text ) ;
283
+ message = errorDoc . RootElement . GetProperty ( "message" ) . GetString ( ) ?? "" ;
284
+ if ( errorDoc . RootElement . TryGetProperty ( "type" , out var typeElement ) )
285
+ {
286
+ type = typeElement . GetString ( ) ?? "" ;
287
+ }
288
+ }
289
+ catch
290
+ {
291
+ message = text ;
292
+ }
279
293
} else {
280
294
message = text ;
281
295
}
282
296
283
297
throw new AppwriteException ( message , code , type , text ) ;
284
298
}
285
299
286
- return response . Headers . Location . OriginalString ;
300
+ return response . Headers . Location ? . OriginalString ?? string . Empty ;
287
301
}
288
302
289
303
public Task < Dictionary < string , object ? > > Call (
@@ -329,8 +343,19 @@ public async Task<T> Call<T>(
329
343
var type = "" ;
330
344
331
345
if ( isJson ) {
332
- message = JObject . Parse ( text ) [ "message" ] ! . ToString ( ) ;
333
- type = JObject . Parse ( text ) [ "type" ] ? . ToString ( ) ?? string . Empty ;
346
+ try
347
+ {
348
+ using var errorDoc = JsonDocument . Parse ( text ) ;
349
+ message = errorDoc . RootElement . GetProperty ( "message" ) . GetString ( ) ?? "" ;
350
+ if ( errorDoc . RootElement . TryGetProperty ( "type" , out var typeElement ) )
351
+ {
352
+ type = typeElement . GetString ( ) ?? "" ;
353
+ }
354
+ }
355
+ catch
356
+ {
357
+ message = text ;
358
+ }
334
359
} else {
335
360
message = text ;
336
361
}
@@ -342,13 +367,13 @@ public async Task<T> Call<T>(
342
367
{
343
368
var responseString = await response . Content . ReadAsStringAsync ( ) ;
344
369
345
- var dict = JsonConvert . DeserializeObject < Dictionary < string , object > > (
370
+ var dict = JsonSerializer . Deserialize < Dictionary < string , object > > (
346
371
responseString ,
347
- DeserializerSettings ) ;
372
+ DeserializerOptions ) ;
348
373
349
- if ( convert != null )
374
+ if ( convert != null && dict != null )
350
375
{
351
- return convert ( dict ! ) ;
376
+ return convert ( dict ) ;
352
377
}
353
378
354
379
return ( dict as T ) ! ;
@@ -368,7 +393,16 @@ public async Task<T> ChunkedUpload<T>(
368
393
string ? idParamName = null ,
369
394
Action < UploadProgress > ? onProgress = null ) where T : class
370
395
{
396
+ if ( string . IsNullOrEmpty ( paramName ) )
397
+ throw new ArgumentException ( "Parameter name cannot be null or empty" , nameof ( paramName ) ) ;
398
+
399
+ if ( ! parameters . ContainsKey ( paramName ) )
400
+ throw new ArgumentException ( $ "Parameter { paramName } not found", nameof ( paramName ) ) ;
401
+
371
402
var input = parameters [ paramName ] as InputFile ;
403
+ if ( input == null )
404
+ throw new ArgumentException ( $ "Parameter { paramName } must be an InputFile", nameof ( paramName ) ) ;
405
+
372
406
var size = 0L ;
373
407
switch ( input . SourceType )
374
408
{
@@ -378,10 +412,16 @@ public async Task<T> ChunkedUpload<T>(
378
412
size = info . Length ;
379
413
break ;
380
414
case "stream" :
381
- size = ( input . Data as Stream ) . Length ;
415
+ var stream = input . Data as Stream ;
416
+ if ( stream == null )
417
+ throw new InvalidOperationException ( "Stream data is null" ) ;
418
+ size = stream . Length ;
382
419
break ;
383
420
case "bytes" :
384
- size = ( ( byte [ ] ) input . Data ) . Length ;
421
+ var bytes = input . Data as byte [ ] ;
422
+ if ( bytes == null )
423
+ throw new InvalidOperationException ( "Byte array data is null" ) ;
424
+ size = bytes . Length ;
385
425
break ;
386
426
} ;
387
427
@@ -395,10 +435,16 @@ public async Task<T> ChunkedUpload<T>(
395
435
{
396
436
case "path" :
397
437
case "stream" :
398
- await ( input . Data as Stream ) . ReadAsync ( buffer , 0 , ( int ) size ) ;
438
+ var dataStream = input . Data as Stream ;
439
+ if ( dataStream == null )
440
+ throw new InvalidOperationException ( "Stream data is null" ) ;
441
+ await dataStream . ReadAsync ( buffer , 0 , ( int ) size ) ;
399
442
break ;
400
443
case "bytes" :
401
- buffer = ( byte [ ] ) input . Data ;
444
+ var dataBytes = input . Data as byte [ ] ;
445
+ if ( dataBytes == null )
446
+ throw new InvalidOperationException ( "Byte array data is null" ) ;
447
+ buffer = dataBytes ;
402
448
break ;
403
449
}
404
450
@@ -424,14 +470,16 @@ public async Task<T> ChunkedUpload<T>(
424
470
// Make a request to check if a file already exists
425
471
var current = await Call < Dictionary < string , object ? > > (
426
472
method : "GET" ,
427
- path : $ "{ path } /{ parameters [ idParamName ] } ",
473
+ path : $ "{ path } /{ parameters [ idParamName ! ] } ",
428
474
new Dictionary < string , string > { { "content-type" , "application/json" } } ,
429
475
parameters : new Dictionary < string , object ? > ( )
430
476
) ;
431
- var chunksUploaded = ( long ) current [ "chunksUploaded" ] ;
432
- offset = chunksUploaded * ChunkSize ;
477
+ if ( current . TryGetValue ( "chunksUploaded" , out var chunksUploadedValue ) && chunksUploadedValue != null )
478
+ {
479
+ offset = Convert . ToInt64 ( chunksUploadedValue ) * ChunkSize ;
480
+ }
433
481
}
434
- catch ( Exception ex )
482
+ catch
435
483
{
436
484
// ignored as it mostly means file not found
437
485
}
@@ -444,6 +492,8 @@ public async Task<T> ChunkedUpload<T>(
444
492
case "path" :
445
493
case "stream" :
446
494
var stream = input . Data as Stream ;
495
+ if ( stream == null )
496
+ throw new InvalidOperationException ( "Stream data is null" ) ;
447
497
stream . Seek ( offset , SeekOrigin . Begin ) ;
448
498
await stream . ReadAsync ( buffer , 0 , ChunkSize ) ;
449
499
break ;
@@ -476,12 +526,12 @@ public async Task<T> ChunkedUpload<T>(
476
526
var id = result . ContainsKey ( "$id" )
477
527
? result [ "$id" ] ? . ToString ( ) ?? string . Empty
478
528
: string . Empty ;
479
- var chunksTotal = result . ContainsKey ( "chunksTotal" )
480
- ? ( long ) result [ "chunksTotal" ]
481
- : 0 ;
482
- var chunksUploaded = result . ContainsKey ( "chunksUploaded" )
483
- ? ( long ) result [ "chunksUploaded" ]
484
- : 0 ;
529
+ var chunksTotal = result . TryGetValue ( "chunksTotal" , out var chunksTotalValue ) && chunksTotalValue != null
530
+ ? Convert . ToInt64 ( chunksTotalValue )
531
+ : 0L ;
532
+ var chunksUploaded = result . TryGetValue ( "chunksUploaded" , out var chunksUploadedValue ) && chunksUploadedValue != null
533
+ ? Convert . ToInt64 ( chunksUploadedValue )
534
+ : 0L ;
485
535
486
536
headers [ "x-appwrite-id" ] = id ;
487
537
@@ -494,7 +544,11 @@ public async Task<T> ChunkedUpload<T>(
494
544
chunksUploaded : chunksUploaded ) ) ;
495
545
}
496
546
497
- return converter ( result ) ;
547
+ // Convert to non-nullable dictionary for converter
548
+ var nonNullableResult = result . Where ( kvp => kvp . Value != null )
549
+ . ToDictionary ( kvp => kvp . Key , kvp => kvp . Value ! ) ;
550
+
551
+ return converter ( nonNullableResult ) ;
498
552
}
499
553
}
500
554
}
0 commit comments