[Breaking]: Normalize ISerializer argument validation and enhance test coverage#440
Conversation
- Add ArgumentNullException.ThrowIfNull() for serializer parameter in all extension methods - Add ArgumentNullException.ThrowIfNull() for data parameter (Stream, byte[]) - Add ArgumentException.ThrowIfNullOrEmpty() for string data parameter - Add ArgumentOutOfRangeException.ThrowIfZero() for empty byte arrays - Rename existing tests to follow MethodName_State_Expected pattern - Add 5 new virtual test methods for comprehensive coverage - Update all 6 concrete serializer test classes with alphabetically ordered tests - Add Arrange/Act/Assert comments to all tests - All 48 serializer tests pass, 1727 total tests pass
…on, and test coverage - Fix resource disposal in SystemTextJsonSerializer (Utf8JsonWriter) and JsonNetSerializer (JsonTextWriter, StreamWriter) - Normalize parameter names across all ISerializer implementations to match interface - Add XML documentation to ISerializer and ITextSerializer interfaces - Add whitespace validation (ThrowIfNullOrWhiteSpace) for string deserialization - Add comprehensive test coverage: empty collections, null properties, DateTime, numeric types - Update serialization documentation with extension methods and validation behavior
Additional Changes (Second Commit)This commit adds further improvements to the serializer infrastructure: 1. Resource Disposal FixesSystemTextJsonSerializer.cs:
JsonNetSerializer.cs:
2. Parameter Name NormalizationAll implementations now match the interface parameter names:
3. Interface DocumentationAdded comprehensive XML documentation to:
4. Whitespace Validation
5. Additional Test CoverageNew tests added to
6. Documentation UpdatesUpdated
Test Results
|
Question: SerializeToBytes/SerializeToString Null HandlingCurrently, when public static byte[] SerializeToBytes<T>(this ISerializer serializer, T value)
{
ArgumentNullException.ThrowIfNull(serializer);
if (value is null)
return null; // <-- Current behavior
var stream = new MemoryStream();
serializer.Serialize(value, stream);
return stream.ToArray();
}Options:
@ejsmith What's your preference? The current behavior is documented but differs from the stricter validation on deserialization methods. |
Performance: - SystemTextJsonSerializer: Use direct stream APIs instead of Utf8JsonWriter/StreamReader - SystemTextJsonSerializer: Fix DateTimeOffset parsing order (try DateTimeOffset before DateTime) - SystemTextJsonSerializer: Improve number parsing (int32 → int64 → decimal → double) Validation: - Add ArgumentNullException validation to all four core ISerializer implementations - Core Serialize: validates output stream (allows null value for serialization) - Core Deserialize: validates data stream and objectType parameters Breaking Change: - SerializeToBytes(null) and SerializeToString(null) now return serialized 'null' literal instead of returning null (aligns with System.Text.Json behavior) Tests: - Add Serialize_WithInvalidArguments_ThrowsArgumentNullException - Add Deserialize_WithInvalidArguments_ThrowsArgumentNullException - Add Serialize_WithNullValue_RoundTripsCorrectly (consolidated null test) - Add Serialize_WithSpecialCharacters_RoundTripsCorrectly (Unicode, emoji, escapes) - Remove obsolete SerializeToBytes_WithNullValue_ReturnsNull - Remove obsolete SerializeToString_WithNullValue_ReturnsNull
Replaces ArgumentException with ArgumentOutOfRangeException for zero-length data validation in ISerializer extension methods. This change streamlines argument validation by using the dedicated ArgumentOutOfRangeException for checking data length, improving code readability and consistency.
Latest Commit: Serializer Performance, Validation, and Consistency ImprovementsPerformance Improvements (SystemTextJsonSerializer)
Core Implementation ValidationAdded Note: Breaking Change
|
…s with breaking changes - Add comprehensive test for numeric primitive deserialization to object type - Change ArgumentOutOfRangeException to ArgumentException for empty byte arrays to match documentation - Update serialization.md with accurate null value behavior and breaking changes section - All tests pass (1769 passed, 11 skipped)
There was a problem hiding this comment.
Pull request overview
This PR normalizes argument validation in the serializer abstraction layer to align with System.Text.Json behavior and significantly enhances test coverage. The changes introduce several breaking changes to improve consistency and correctness.
Changes:
- Added argument validation to all
SerializerExtensionsmethods using modern .NET throw helpers - Changed null value serialization behavior to return serialized representations instead of null
- Enhanced
SystemTextJsonSerializerprimitive type handling to return more specific types (int instead of long, decimal instead of double in certain cases) - Expanded test suite from 3 to 15 tests per serializer implementation
- Added comprehensive XML documentation to interfaces
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Foundatio/Serializer/ISerializer.cs | Added argument validation and XML documentation to all extension methods |
| src/Foundatio/Serializer/SystemTextJsonSerializer.cs | Refactored to use direct stream serialization and improved primitive type coercion logic |
| src/Foundatio.JsonNet/JsonNetSerializer.cs | Added argument validation and updated resource disposal pattern |
| src/Foundatio.MessagePack/MessagePackSerializer.cs | Added argument validation to core methods |
| src/Foundatio.Utf8Json/Utf8JsonSerializer.cs | Added argument validation to core methods |
| src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs | Expanded from 3 to 15 test methods with comprehensive validation coverage |
| tests/Foundatio.Tests/Serializer/*.cs | Updated all 6 concrete serializer test classes with full test method overrides |
| docs/guide/serialization.md | Added documentation for extension methods and validation behavior |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Documents that ISerializer implementations should handle null values, e.g., by serializing them as "null" or nil markers. This ensures consistent behavior across different serializers.
Serializer Test Coverage Enhancement
Summary
SerializerExtensionsmethodsBreaking Changes
The following breaking changes were introduced to improve consistency and correctness:
Null Value Serialization
SerializeToBytes(null)null[110, 117, 108, 108]for JSON "null")SerializeToString(null)null"null"string for JSON serializersDeserialize<T>((string)null)ArgumentExceptionDeserialize<T>("")ArgumentExceptionMigration: If your code checks for
nullreturn values from serialization methods, update it to handle the serialized null representation instead.SystemTextJsonSerializer Primitive Type Handling
When deserializing to
objecttype, the returned types for primitives have changed:longintlonglongdoubledecimal(when precise)DateTimeDateTimeOffset(preserves timezone)Migration: If your code performs type checks like
if (value is long), update to handle the new types or use pattern matching with multiple types.Part 1: Update SerializerExtensions
File:
src/Foundatio/Serializer/ISerializer.csArgumentNullException.ThrowIfNull()forserializerparameter in all extension methodsArgumentNullException.ThrowIfNull()fordataparameter (Stream, byte[])ArgumentException.ThrowIfNullOrEmpty()for stringdataparameterArgumentOutOfRangeException.ThrowIfZero()toArgumentExceptionfor empty byte arrays (consistency with documentation)is nullpattern matchingPart 2: Rename Existing Tests
CanRoundTripBytesDeserialize_WithValidBytes_ReturnsDeserializedObjectCanRoundTripStringDeserialize_WithValidString_ReturnsDeserializedObjectCanHandlePrimitiveTypesDeserialize_WithPrimitiveType_ReturnsValuePart 3: New Tests in SerializerTestsBase
File:
src/Foundatio.TestHarness/Serializer/SerializerTestsBase.csAdded comprehensive virtual test methods:
Deserialize_WithInvalidInput_ThrowsArgumentException- grouped validation testDeserialize_WithUnicodeAndSpecialCharacters_PreservesContent- Unicode testDeserialize_WithValidStream_ReturnsDeserializedObject- stream overload testDeserialize_WithNumericPrimitivesToObject_ReturnsCorrectTypes- numeric type coercion testSerializeToBytes_WithNullValue_ReturnsNull- null value testSerializeToString_WithNullValue_ReturnsNull- null value testAll tests follow the Arrange/Act/Assert pattern with proper comments.
Part 4: Updated All Concrete Test Classes
Updated all 6 serializer test classes with alphabetically ordered test overrides:
CompressedMessagePackSerializerTests.csJsonNetSerializerTests.csMessagePackSerializerTests.csSystemTextJsonSerializerTests.cs(both classes)Utf8JsonSerializerTests.csEach concrete test class now has comprehensive test coverage.
Documentation Updates
File:
docs/guide/serialization.mdTest Results
Code Style
Assertstatements}ISerializer/ITextSerializerinterfaceDeserialize