Enhance JSON converters for DateTime, DateTimeOffset, and TimeSpan#33
Conversation
…r DateTime, DateTimeOffset, and TimeSpan types
…t, and TimeSpan types
There was a problem hiding this comment.
Pull request overview
This PR enhances JSON serialization for temporal types (DateTime, DateTimeOffset, TimeSpan) by standardizing their serialization format and adding comprehensive validation tests.
Key Changes:
- Changed serialization format from numeric (ticks/Unix milliseconds) to ISO 8601 string format for DateTime and DateTimeOffset, and constant format for TimeSpan
- Added null handling in Write methods for all three converters
- Updated DateTimeStyles from None to RoundtripKind for better timezone preservation during parsing
- Added comprehensive schema validation tests with round-trip serialization verification
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
ReflectorNet/src/Convertor/Json/TimeSpanJsonConverter.cs |
Changed TimeSpan serialization from numeric ticks to constant format string ("c"); updated documentation to reflect format change |
ReflectorNet/src/Convertor/Json/DateTimeOffsetJsonConverter.cs |
Changed serialization from Unix milliseconds (number) to ISO 8601 string ("o"); improved variable naming; updated DateTimeStyles to RoundtripKind |
ReflectorNet/src/Convertor/Json/DateTimeJsonConverter.cs |
Changed serialization from ticks to ISO 8601 string; updated numeric parsing to use Unix milliseconds; switched to RoundtripKind DateTimeStyles |
ReflectorNet.Tests/src/SchemaTests/SchemaSerializationValidationTests.cs |
Added comprehensive test suite validating schema generation, serialization/deserialization round-trips, and schema conformance for temporal types and collections |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Array) | ||
| { | ||
| Assert.True(valueNode is JsonArray, | ||
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.String) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); |
There was a problem hiding this comment.
Condition is always not null because of ... == ....
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| $"Schema declares type 'object' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); |
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Array) | ||
| { | ||
| Assert.True(valueNode is JsonArray, | ||
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.String) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Boolean) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); |
There was a problem hiding this comment.
Condition is always not null because of ... == ....
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| $"Schema declares type 'object' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); |
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Array) | ||
| { | ||
| Assert.True(valueNode is JsonArray, | ||
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.String) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Boolean) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); |
There was a problem hiding this comment.
Condition is always not null because of ... == ....
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| $"Schema declares type 'object' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); |
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Array) | ||
| { | ||
| Assert.True(valueNode is JsonArray, | ||
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.String) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Boolean) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); |
There was a problem hiding this comment.
Condition is always not null because of ... == ....
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| $"Schema declares type 'object' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); |
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Array) | ||
| { | ||
| Assert.True(valueNode is JsonArray, | ||
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.String) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | ||
| } | ||
| else if (schemaType == JsonSchema.Boolean) | ||
| { | ||
| Assert.True(valueNode is JsonValue, | ||
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); |
There was a problem hiding this comment.
Condition is always not null because of ... == ....
| $"Schema declares type 'object' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode?.GetType().Name}. JSON: {valueNode}"); | |
| $"Schema declares type 'object' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Array) | |
| { | |
| Assert.True(valueNode is JsonArray, | |
| $"Schema declares type 'array' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.String) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'string' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Number || schemaType == JsonSchema.Integer) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type '{schemaType}' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); | |
| } | |
| else if (schemaType == JsonSchema.Boolean) | |
| { | |
| Assert.True(valueNode is JsonValue, | |
| $"Schema declares type 'boolean' but JSON value is {valueNode.GetType().Name}. JSON: {valueNode}"); |
Improve JSON converters to handle number values and nulls for DateTime, DateTimeOffset, and TimeSpan types. Add validation tests to ensure proper schema serialization.