Skip to content

Enhance JSON converters for DateTime, DateTimeOffset, and TimeSpan#33

Merged
IvanMurzak merged 3 commits into
mainfrom
fix/date-time-json-serialization
Dec 5, 2025
Merged

Enhance JSON converters for DateTime, DateTimeOffset, and TimeSpan#33
IvanMurzak merged 3 commits into
mainfrom
fix/date-time-json-serialization

Conversation

@IvanMurzak
Copy link
Copy Markdown
Owner

Improve JSON converters to handle number values and nulls for DateTime, DateTimeOffset, and TimeSpan types. Add validation tests to ensure proper schema serialization.

@IvanMurzak IvanMurzak requested a review from Copilot December 5, 2025 09:31
@IvanMurzak IvanMurzak self-assigned this Dec 5, 2025
@IvanMurzak IvanMurzak added bug Something isn't working enhancement New feature or request labels Dec 5, 2025
@IvanMurzak IvanMurzak merged commit ec079f1 into main Dec 5, 2025
1 check passed
@IvanMurzak IvanMurzak deleted the fix/date-time-json-serialization branch December 5, 2025 09:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread ReflectorNet/src/Convertor/Json/DateTimeJsonConverter.cs
Comment on lines +141 to +156
$"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}");
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition is always not null because of ... == ....

Suggested change
$"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}");

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +161
$"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}");
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition is always not null because of ... == ....

Suggested change
$"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}");

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +161
$"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}");
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition is always not null because of ... == ....

Suggested change
$"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}");

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +161
$"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}");
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition is always not null because of ... == ....

Suggested change
$"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}");

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +161
$"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}");
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition is always not null because of ... == ....

Suggested change
$"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}");

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants