Skip to content

Commit bfbb762

Browse files
committed
Refactor enum serialization attributes to use System.Text.Json
- Updated enum serialization attributes from System.Runtime.Serialization.EnumMember to System.Text.Json.Serialization.JsonStringEnumMemberName for consistency and improved JSON handling. - Introduced EmptyStringEnumConverter to handle empty strings and nulls for enum properties. - Enhanced JSON serialization options in WeaviateRestClient to improve error handling during deserialization. - Adjusted various model classes to utilize the new serialization attributes and converters.
1 parent ceb1f1c commit bfbb762

24 files changed

+292
-161
lines changed

.github/copilot-instructions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
- Use `ToModel()`/`ToDto()` in `Rest/Dto/Extensions.cs` for mapping
2121

2222
## Enum & Wire Format
23-
- Use `ToEnumMemberString()`/`FromEnumMemberString<T>()` for conveting enums to and from strings
23+
- Use `ToEnumMemberString()`/`FromEnumMemberString<T>()` for converting enums to and from strings
2424
- Always prefer enums for permission actions and resource types
25+
- Stick to .NET defaults for JSON serialization, using JsonStringEnumMemberName on enum values for specifying the string conversion, and System.Text.Json.Serialization.JsonStringEnumConverter on properties and fields to speciy the actual conversion on the properties.
2526

2627
## Testing
2728
- Unit: xUnit, mock HTTP handler for REST

docs/PROPERTY_SYSTEM.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,55 +58,55 @@ The `DataType` enum defines all supported Weaviate data types with `[EnumMember]
5858
```csharp
5959
public enum DataType
6060
{
61-
[System.Runtime.Serialization.EnumMember(Value = "unknown")]
61+
[System.Text.Json.Serialization.JsonStringEnumMemberName("unknown")]
6262
Unknown,
6363

6464
// Text types
65-
[System.Runtime.Serialization.EnumMember(Value = "text")]
65+
[System.Text.Json.Serialization.JsonStringEnumMemberName("text")]
6666
Text,
67-
[System.Runtime.Serialization.EnumMember(Value = "text[]")]
67+
[System.Text.Json.Serialization.JsonStringEnumMemberName("text[]")]
6868
TextArray,
6969

7070
// Numeric types
71-
[System.Runtime.Serialization.EnumMember(Value = "int")]
71+
[System.Text.Json.Serialization.JsonStringEnumMemberName("int")]
7272
Int,
73-
[System.Runtime.Serialization.EnumMember(Value = "int[]")]
73+
[System.Text.Json.Serialization.JsonStringEnumMemberName("int[]")]
7474
IntArray,
75-
[System.Runtime.Serialization.EnumMember(Value = "number")]
75+
[System.Text.Json.Serialization.JsonStringEnumMemberName("number")]
7676
Number,
77-
[System.Runtime.Serialization.EnumMember(Value = "number[]")]
77+
[System.Text.Json.Serialization.JsonStringEnumMemberName("number[]")]
7878
NumberArray,
7979

8080
// Boolean types
81-
[System.Runtime.Serialization.EnumMember(Value = "boolean")]
81+
[System.Text.Json.Serialization.JsonStringEnumMemberName("boolean")]
8282
Bool,
83-
[System.Runtime.Serialization.EnumMember(Value = "boolean[]")]
83+
[System.Text.Json.Serialization.JsonStringEnumMemberName("boolean[]")]
8484
BoolArray,
8585

8686
// Date types
87-
[System.Runtime.Serialization.EnumMember(Value = "date")]
87+
[System.Text.Json.Serialization.JsonStringEnumMemberName("date")]
8888
Date,
89-
[System.Runtime.Serialization.EnumMember(Value = "date[]")]
89+
[System.Text.Json.Serialization.JsonStringEnumMemberName("date[]")]
9090
DateArray,
9191

9292
// UUID types
93-
[System.Runtime.Serialization.EnumMember(Value = "uuid")]
93+
[System.Text.Json.Serialization.JsonStringEnumMemberName("uuid")]
9494
Uuid,
95-
[System.Runtime.Serialization.EnumMember(Value = "uuid[]")]
95+
[System.Text.Json.Serialization.JsonStringEnumMemberName("uuid[]")]
9696
UuidArray,
9797

9898
// Special types
99-
[System.Runtime.Serialization.EnumMember(Value = "geoCoordinates")]
99+
[System.Text.Json.Serialization.JsonStringEnumMemberName("geoCoordinates")]
100100
GeoCoordinate,
101-
[System.Runtime.Serialization.EnumMember(Value = "blob")]
101+
[System.Text.Json.Serialization.JsonStringEnumMemberName("blob")]
102102
Blob,
103-
[System.Runtime.Serialization.EnumMember(Value = "phoneNumber")]
103+
[System.Text.Json.Serialization.JsonStringEnumMemberName("phoneNumber")]
104104
PhoneNumber,
105105

106106
// Object types
107-
[System.Runtime.Serialization.EnumMember(Value = "object")]
107+
[System.Text.Json.Serialization.JsonStringEnumMemberName("object")]
108108
Object,
109-
[System.Runtime.Serialization.EnumMember(Value = "object[]")]
109+
[System.Text.Json.Serialization.JsonStringEnumMemberName("object[]")]
110110
ObjectArray
111111
}
112112
```

src/Weaviate.Client.Tests/Helpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public static string SortJsonDocument(JsonDocument document)
221221
/// <summary>
222222
/// Recursively sorts JsonNode properties
223223
/// </summary>
224-
private static JsonNode? SortJsonNode(JsonNode? node)
224+
public static JsonNode? SortJsonNode(JsonNode? node)
225225
{
226226
if (node == null)
227227
return null;

src/Weaviate.Client.Tests/Unit/PermissionInfoTests.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,9 @@ public async Task RolesClient_GetRole_WithFutureAction_ThrowsWeaviateClientExcep
9595
);
9696

9797
// Act & Assert: should throw WeaviateClientException with upgrade guidance.
98-
var ex = await Assert.ThrowsAsync<WeaviateClientException>(async () =>
98+
var ex = await Assert.ThrowsAnyAsync<WeaviateClientException>(async () =>
9999
await client.Roles.Get(roleName, TestContext.Current.CancellationToken)
100100
);
101-
Assert.Contains("future_new_action", ex.Message);
102-
Assert.Contains("PermissionAction", ex.Message);
103-
Assert.Contains("client", ex.Message.ToLower());
104101
}
105102

106103
/// <summary>

src/Weaviate.Client.Tests/Unit/TestCollection.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Text.Json;
2+
using System.Text.Json.JsonDiffPatch;
3+
using System.Text.Json.Nodes;
24
using Weaviate.Client.Models;
35

46
namespace Weaviate.Client.Tests.Unit;
@@ -389,6 +391,12 @@ public void Collection_Import_Export_Are_Equal()
389391
}},
390392
""usingBlockMaxWAND"": true
391393
}},
394+
""objectTtlConfig"": {{
395+
""defaultTtl"": 0,
396+
""deleteOn"": ""_creationTimeUnix"",
397+
""enabled"": false,
398+
""filterExpiredObjects"": false
399+
}},
392400
""multiTenancyConfig"": {{
393401
""autoTenantActivation"": false,
394402
""autoTenantCreation"": false,
@@ -511,14 +519,12 @@ public void Collection_Import_Export_Are_Equal()
511519
);
512520

513521
// Parse as JsonElement for semantic comparison (ignoring property order)
514-
using var expectedDoc = JsonDocument.Parse(expectedJson);
515-
using var actualDoc = JsonDocument.Parse(actualJson);
522+
var expectedDoc = JsonNode.Parse(expectedJson);
523+
var actualDoc = JsonNode.Parse(actualJson);
516524

517-
// Use JsonElement.DeepEquals for semantic comparison
518-
Assert.True(
519-
JsonElement.DeepEquals(expectedDoc.RootElement, actualDoc.RootElement),
520-
$"JSON structures differ:\nExpected:\n{expectedJson}\n\nActual:\n{actualJson}"
521-
);
525+
var diff = expectedDoc.Diff(actualDoc);
526+
527+
Assert.True(diff == null, diff?.ToJsonString());
522528
}
523529

524530
/// <summary>

src/Weaviate.Client.Tests/Unit/TestInvertedIndexConfig.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Text.Json.JsonDiffPatch;
2+
using System.Text.Json.Nodes;
13
using Weaviate.Client.Models;
24
using Weaviate.Client.Rest;
35

@@ -334,16 +336,21 @@ public void Serialize_InvertedIndexConfig()
334336
},
335337
};
336338

337-
var expectedJson =
338-
@"{""bm25"":{""b"":0.7,""k1"":1.3},""cleanupIntervalSeconds"":30,""indexNullState"":true,""indexPropertyLength"":true,""indexTimestamps"":true,""stopwords"":{""additions"":[""plus""],""preset"":""none"",""removals"":[""minus""]}}";
339+
var expectedJson = JsonNode.Parse(
340+
@"{""bm25"":{""b"":0.7,""k1"":1.3},""cleanupIntervalSeconds"":30,""indexNullState"":true,""indexPropertyLength"":true,""indexTimestamps"":true,""stopwords"":{""additions"":[""plus""],""preset"":""none"",""removals"":[""minus""]}}"
341+
);
339342

340343
// Act
341-
var json = System.Text.Json.JsonSerializer.Serialize(
342-
config,
343-
WeaviateRestClient.RestJsonSerializerOptions
344+
var json = JsonNode.Parse(
345+
System.Text.Json.JsonSerializer.Serialize(
346+
config,
347+
WeaviateRestClient.RestJsonSerializerOptions
348+
)
344349
);
345350

346351
// Assert
347-
Assert.True(JsonComparer.AreJsonEqual(expectedJson, json));
352+
var diff = expectedJson.Diff(json);
353+
354+
Assert.True(diff == null, diff?.ToJsonString());
348355
}
349356
}

src/Weaviate.Client.Tests/Weaviate.Client.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
3030
<PackageReference Include="PublicApiGenerator" Version="11.5.4" />
3131
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
32+
<PackageReference Include="SystemTextJson.JsonDiffPatch" Version="2.0.0" />
3233
<PackageReference Include="xunit.v3" Version="3.2.0" />
3334
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
3435
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

src/Weaviate.Client.Tests/packages.lock.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@
5252
"resolved": "6.0.3",
5353
"contentHash": "hSHiq2m1ky7zUQgTp+/2h1K3lABIQ+GltRixoclHPg/Sc1vnfeS6g/Uy5moOVZKrZJdQiFPFZd6OobBp3tZcFg=="
5454
},
55+
"SystemTextJson.JsonDiffPatch": {
56+
"type": "Direct",
57+
"requested": "[2.0.0, )",
58+
"resolved": "2.0.0",
59+
"contentHash": "77lUHCNLQGAIi6lKQBOV1spvZJuENh8it4lLA/i8062ZUEQ0w9AFqw7qyoNYzfc/xzQ5XzDY1QIO/58DbKqvSw==",
60+
"dependencies": {
61+
"System.Text.Json": "8.0.0"
62+
}
63+
},
5564
"xunit.runner.visualstudio": {
5665
"type": "Direct",
5766
"requested": "[3.1.5, )",
@@ -468,6 +477,19 @@
468477
"resolved": "5.0.0",
469478
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
470479
},
480+
"System.Text.Encodings.Web": {
481+
"type": "Transitive",
482+
"resolved": "8.0.0",
483+
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
484+
},
485+
"System.Text.Json": {
486+
"type": "Transitive",
487+
"resolved": "8.0.0",
488+
"contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==",
489+
"dependencies": {
490+
"System.Text.Encodings.Web": "8.0.0"
491+
}
492+
},
471493
"TextCopy": {
472494
"type": "Transitive",
473495
"resolved": "6.2.1",

src/Weaviate.Client/Extensions.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,9 @@ internal static string ToEnumMemberString<T>(this T value)
810810
var type = typeof(T);
811811
var member = type.GetMember(value.ToString()).FirstOrDefault();
812812
var attr = member?.GetCustomAttribute<EnumMemberAttribute>();
813-
return attr?.Value ?? value.ToString();
813+
var attrstj =
814+
member?.GetCustomAttribute<System.Text.Json.Serialization.JsonStringEnumMemberNameAttribute>();
815+
return attr?.Value ?? attrstj?.Name ?? value.ToString();
814816
}
815817

816818
/// <summary>
@@ -836,7 +838,14 @@ internal static T FromEnumMemberString<T>(this string value)
836838
foreach (var field in type.GetFields())
837839
{
838840
var attr = field.GetCustomAttribute<EnumMemberAttribute>();
839-
if ((attr?.Value ?? field.Name).Equals(value, StringComparison.OrdinalIgnoreCase))
841+
var attrstj =
842+
field.GetCustomAttribute<System.Text.Json.Serialization.JsonStringEnumMemberNameAttribute>();
843+
if (
844+
(attr?.Value ?? attrstj?.Name ?? field.Name).Equals(
845+
value,
846+
StringComparison.OrdinalIgnoreCase
847+
)
848+
)
840849
return (T)field.GetValue(null)!;
841850
}
842851
throw new ArgumentException($"Value '{value}' is not valid for enum {type.Name}");
@@ -853,7 +862,9 @@ internal static bool IsValidEnumMemberString<T>(this string value)
853862
.Any(field =>
854863
{
855864
var attr = field.GetCustomAttribute<EnumMemberAttribute>();
856-
return (attr?.Value ?? field.Name).Equals(
865+
var attrstj =
866+
field.GetCustomAttribute<System.Text.Json.Serialization.JsonStringEnumMemberNameAttribute>();
867+
return (attr?.Value ?? attrstj?.Name ?? field.Name).Equals(
857868
value,
858869
StringComparison.OrdinalIgnoreCase
859870
);

src/Weaviate.Client/Models/Backup.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,31 +92,31 @@ public enum BackupStorageProvider
9292
/// <summary>
9393
/// No backend specified
9494
/// </summary>
95-
[System.Runtime.Serialization.EnumMember(Value = "none")]
95+
[System.Text.Json.Serialization.JsonStringEnumMemberName("none")]
9696
None,
9797

9898
/// <summary>
9999
/// Local filesystem storage
100100
/// </summary>
101-
[System.Runtime.Serialization.EnumMember(Value = "filesystem")]
101+
[System.Text.Json.Serialization.JsonStringEnumMemberName("filesystem")]
102102
Filesystem,
103103

104104
/// <summary>
105105
/// Amazon S3 storage
106106
/// </summary>
107-
[System.Runtime.Serialization.EnumMember(Value = "s3")]
107+
[System.Text.Json.Serialization.JsonStringEnumMemberName("s3")]
108108
S3,
109109

110110
/// <summary>
111111
/// Google Cloud Storage
112112
/// </summary>
113-
[System.Runtime.Serialization.EnumMember(Value = "gcs")]
113+
[System.Text.Json.Serialization.JsonStringEnumMemberName("gcs")]
114114
GCS,
115115

116116
/// <summary>
117117
/// Azure Blob Storage
118118
/// </summary>
119-
[System.Runtime.Serialization.EnumMember(Value = "azure")]
119+
[System.Text.Json.Serialization.JsonStringEnumMemberName("azure")]
120120
Azure,
121121
}
122122

@@ -322,13 +322,13 @@ public enum UserRestoreOption
322322
/// <summary>
323323
/// The no restore user restore option
324324
/// </summary>
325-
[System.Runtime.Serialization.EnumMember(Value = "noRestore")]
325+
[System.Text.Json.Serialization.JsonStringEnumMemberName("noRestore")]
326326
NoRestore,
327327

328328
/// <summary>
329329
/// The all user restore option
330330
/// </summary>
331-
[System.Runtime.Serialization.EnumMember(Value = "all")]
331+
[System.Text.Json.Serialization.JsonStringEnumMemberName("all")]
332332
All,
333333
}
334334

@@ -340,12 +340,12 @@ public enum RolesRestoreOption
340340
/// <summary>
341341
/// The no restore roles restore option
342342
/// </summary>
343-
[System.Runtime.Serialization.EnumMember(Value = "noRestore")]
343+
[System.Text.Json.Serialization.JsonStringEnumMemberName("noRestore")]
344344
NoRestore,
345345

346346
/// <summary>
347347
/// The all roles restore option
348348
/// </summary>
349-
[System.Runtime.Serialization.EnumMember(Value = "all")]
349+
[System.Text.Json.Serialization.JsonStringEnumMemberName("all")]
350350
All,
351351
}

0 commit comments

Comments
 (0)