-
Notifications
You must be signed in to change notification settings - Fork 3
Fix SerializedMember JSON Schema to accept any value type #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
fa75f62
Initial plan
Copilot ed028ca
Fix SerializedMember JSON schema to accept any value type
Copilot 7f0a91a
Improve numeric type detection in SerializedMemberSchemaTests
Copilot 985a7d4
Remove unnecessary value name from SerializedMember JSON schema requi…
IvanMurzak 2bc71f9
Enable inclusion of public fields in JSON serialization
IvanMurzak 451d60b
Simplify JSON schema output formatting in SerializedMemberSchemaTests
IvanMurzak 672865c
Refactor SerializedMemberSchemaTests to remove unnecessary using dire…
IvanMurzak 94f422a
Fix JSON value type detection order in GetJsonValueType method
IvanMurzak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
225 changes: 225 additions & 0 deletions
225
ReflectorNet.Tests/src/SchemaTests/SerializedMemberSchemaTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| using System.Text.Json.Nodes; | ||
| using com.IvanMurzak.ReflectorNet.Model; | ||
| using com.IvanMurzak.ReflectorNet.Utils; | ||
| using Xunit.Abstractions; | ||
|
|
||
| namespace com.IvanMurzak.ReflectorNet.Tests.SchemaTests | ||
| { | ||
| /// <summary> | ||
| /// Tests to validate that SerializedMember JSON schema correctly represents | ||
| /// the actual serialized JSON for various value types. | ||
| /// </summary> | ||
| public class SerializedMemberSchemaTests : BaseTest | ||
| { | ||
| public SerializedMemberSchemaTests(ITestOutputHelper output) : base(output) { } | ||
|
|
||
| [Fact] | ||
| public void SerializedMember_Schema_Value_Should_Accept_Any_JsonType() | ||
| { | ||
| // Arrange | ||
| var reflector = new Reflector(); | ||
| var schema = reflector.GetSchema<SerializedMember>(); | ||
|
|
||
| // Assert - Check that schema was generated | ||
| Assert.NotNull(schema); | ||
|
|
||
| // Get the value property schema | ||
| var schemaObj = schema.AsObject(); | ||
| Assert.True(schemaObj.TryGetPropertyValue(JsonSchema.Properties, out var propertiesNode)); | ||
| var properties = propertiesNode!.AsObject(); | ||
| Assert.True(properties.TryGetPropertyValue(SerializedMember.ValueName, out var valueSchemaNode)); | ||
| var valueSchema = valueSchemaNode!.AsObject(); | ||
|
|
||
| // The value schema should NOT have a "type" constraint | ||
| // This allows it to accept any JSON value type | ||
| Assert.False(valueSchema.TryGetPropertyValue(JsonSchema.Type, out _), | ||
| "The 'value' property schema should not have a 'type' constraint to allow any JSON value type"); | ||
|
|
||
| // It should have a description | ||
| Assert.True(valueSchema.TryGetPropertyValue(JsonSchema.Description, out var descriptionNode)); | ||
| Assert.NotNull(descriptionNode); | ||
|
|
||
| _output.WriteLine($"✓ SerializedMember schema allows 'value' to be any JSON type"); | ||
| _output.WriteLine($"Schema:\n{schema.ToJsonString()}"); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData("test string", "string")] | ||
| [InlineData(42, "number")] | ||
| [InlineData(3.14, "number")] | ||
| [InlineData(true, "boolean")] | ||
| [InlineData(false, "boolean")] | ||
| public void SerializedMember_Should_Serialize_PrimitiveTypes_Correctly(object value, string expectedJsonType) | ||
| { | ||
| // Arrange | ||
| var reflector = new Reflector(); | ||
|
|
||
| // Act | ||
| var serialized = reflector.Serialize(value, name: "testValue"); | ||
| var json = serialized.ToJson(reflector); | ||
|
|
||
| // Parse and validate JSON structure | ||
| var jsonNode = JsonNode.Parse(json); | ||
| Assert.NotNull(jsonNode); | ||
|
|
||
| var jsonObj = jsonNode!.AsObject(); | ||
| Assert.True(jsonObj.TryGetPropertyValue(SerializedMember.ValueName, out var valueNode)); | ||
| Assert.NotNull(valueNode); | ||
|
|
||
| // Validate the value type matches expected | ||
| var actualJsonType = GetJsonValueType(valueNode); | ||
| _output.WriteLine($"Value: {value}, Expected type: {expectedJsonType}, Actual type: {actualJsonType}"); | ||
| _output.WriteLine($"Serialized JSON: {json}"); | ||
|
|
||
| Assert.Equal(expectedJsonType, actualJsonType); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SerializedMember_Should_Serialize_Null_Correctly() | ||
| { | ||
| // Arrange | ||
| var reflector = new Reflector(); | ||
|
|
||
| // Act | ||
| var serialized = reflector.Serialize(null, typeof(string), name: "testValue"); | ||
| var json = serialized.ToJson(reflector); | ||
|
|
||
| // Parse and validate JSON structure | ||
| var jsonNode = JsonNode.Parse(json); | ||
| Assert.NotNull(jsonNode); | ||
|
|
||
| var jsonObj = jsonNode!.AsObject(); | ||
|
|
||
| // For null values, the value property should be absent or null | ||
| if (jsonObj.TryGetPropertyValue(SerializedMember.ValueName, out var valueNode)) | ||
| { | ||
| Assert.Null(valueNode); | ||
| } | ||
|
|
||
| _output.WriteLine($"Serialized null value JSON: {json}"); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SerializedMember_Should_Serialize_Array_Correctly() | ||
| { | ||
| // Arrange | ||
| var reflector = new Reflector(); | ||
| var array = new[] { 1, 2, 3 }; | ||
|
|
||
| // Act | ||
| var serialized = reflector.Serialize(array, name: "testArray"); | ||
| var json = serialized.ToJson(reflector); | ||
|
|
||
| // Parse and validate JSON structure | ||
| var jsonNode = JsonNode.Parse(json); | ||
| Assert.NotNull(jsonNode); | ||
|
|
||
| var jsonObj = jsonNode!.AsObject(); | ||
| Assert.True(jsonObj.TryGetPropertyValue(SerializedMember.ValueName, out var valueNode)); | ||
| Assert.NotNull(valueNode); | ||
|
|
||
| // The value should be a JSON array | ||
| Assert.IsType<JsonArray>(valueNode); | ||
|
|
||
| _output.WriteLine($"Serialized array JSON: {json}"); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SerializedMember_Should_Serialize_Object_Correctly() | ||
| { | ||
| // Arrange | ||
| var reflector = new Reflector(); | ||
| var obj = new TestClass { Name = "Test", Value = 123 }; | ||
|
|
||
| // Act | ||
| var serialized = reflector.Serialize(obj, name: "testObject"); | ||
| var json = serialized.ToJson(reflector); | ||
|
|
||
| // Parse and validate JSON structure | ||
| var jsonNode = JsonNode.Parse(json); | ||
| Assert.NotNull(jsonNode); | ||
|
|
||
| var jsonObj = jsonNode!.AsObject(); | ||
| Assert.True(jsonObj.TryGetPropertyValue(SerializedMember.ValueName, out var valueNode)); | ||
| Assert.NotNull(valueNode); | ||
|
|
||
| // For complex objects, the value is typically an empty object {} | ||
| // and the actual data is in props/fields | ||
| Assert.IsType<JsonObject>(valueNode); | ||
|
|
||
| _output.WriteLine($"Serialized object JSON: {json}"); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SerializedMember_RoundTrip_Should_Preserve_AllValueTypes() | ||
| { | ||
| // Arrange | ||
| var reflector = new Reflector(); | ||
| var testValues = new object[] | ||
| { | ||
| "string value", | ||
| 42, | ||
| 3.14159, | ||
| true, | ||
| false | ||
| }; | ||
|
|
||
| foreach (var originalValue in testValues) | ||
| { | ||
| // Act | ||
| var serialized = reflector.Serialize(originalValue, name: "test"); | ||
| var deserialized = reflector.Deserialize(serialized); | ||
|
|
||
| // Assert | ||
| var originalJson = originalValue.ToJson(reflector); | ||
| var deserializedJson = deserialized.ToJson(reflector); | ||
|
|
||
| _output.WriteLine($"Original: {originalValue} ({originalValue.GetType().Name})"); | ||
| _output.WriteLine($"Original JSON: {originalJson}"); | ||
| _output.WriteLine($"Deserialized JSON: {deserializedJson}"); | ||
|
|
||
| Assert.Equal(originalJson, deserializedJson); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Helper method to determine the JSON value type from a JsonNode | ||
| /// </summary> | ||
| private string GetJsonValueType(JsonNode node) | ||
| { | ||
| return node switch | ||
| { | ||
| JsonObject => "object", | ||
| JsonArray => "array", | ||
| JsonValue value when value.TryGetValue<bool>(out _) => "boolean", | ||
| JsonValue value when IsNumericJsonValue(value) => "number", | ||
| JsonValue value when value.TryGetValue<string>(out _) => "string", | ||
| _ => "unknown" | ||
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Helper method to check if a JsonValue represents a numeric type | ||
| /// </summary> | ||
| private bool IsNumericJsonValue(JsonValue value) | ||
| { | ||
| return value.TryGetValue<int>(out _) || | ||
| value.TryGetValue<long>(out _) || | ||
| value.TryGetValue<float>(out _) || | ||
| value.TryGetValue<double>(out _) || | ||
| value.TryGetValue<decimal>(out _) || | ||
| value.TryGetValue<byte>(out _) || | ||
| value.TryGetValue<short>(out _) || | ||
| value.TryGetValue<uint>(out _) || | ||
| value.TryGetValue<ulong>(out _) || | ||
| value.TryGetValue<ushort>(out _) || | ||
| value.TryGetValue<sbyte>(out _); | ||
| } | ||
|
|
||
| private class TestClass | ||
| { | ||
| public string Name { get; set; } = string.Empty; | ||
| public int Value { get; set; } | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.