Skip to content

Nullable Serialization#128

Merged
twogood merged 2 commits into
mainfrom
nullable-serialization2
Oct 25, 2025
Merged

Nullable Serialization#128
twogood merged 2 commits into
mainfrom
nullable-serialization2

Conversation

@twogood

@twogood twogood commented Oct 25, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced null handling in serialization to prevent potential errors when processing null inputs.
  • Refactor

    • Strengthened null-safety across serialization and deserialization components to provide more robust handling of edge cases and null values, improving overall API reliability.

@twogood twogood self-assigned this Oct 25, 2025
@coderabbitai

coderabbitai Bot commented Oct 25, 2025

Copy link
Copy Markdown

Walkthrough

The PR enables nullable reference types across the serialization framework by updating method signatures to accept nullable parameters (object?), return nullable types (Task<object?>, ISerializer?, IDeserializer?), removing #nullable disable directives, and adding null-safety checks in implementations where necessary.

Changes

Cohort / File(s) Summary
Serialization interfaces
Activout.RestClient/Serialization/ISerializer.cs, Activout.RestClient/Serialization/IDeserializer.cs, Activout.RestClient/Serialization/ISerializationManager.cs
Updated method signatures to accept/return nullable types. ISerializer.Serialize now accepts object? instead of object. IDeserializer.Deserialize returns Task<object?> instead of Task<object>. ISerializationManager.GetSerializer and GetDeserializer return nullable interface types. Removed #nullable disable directives and flattened nested interface structure.
Text-based serializers
Activout.RestClient.Json/SystemTextJsonSerializer.cs, Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs, Activout.RestClient/Serialization/Implementation/StringSerializer.cs
Updated Serialize method to accept object? data. StringSerializer added null-safe handling using data?.ToString() ?? "" to prevent null references.
Binary and encoded serializers
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs, Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs
Updated Serialize method signatures to accept object? data. FormUrlEncodedSerializer added null-forgiving operator and enhanced SerializeValue to return empty string instead of null.
XML serializers
Activout.RestClient.Xml/XmlSerializer.cs, Activout.RestClient.Xml/XmlDeserializer.cs
XmlSerializer updated to accept object? with null check returning empty StringContent. XmlDeserializer return type changed to Task<object?>.
String and byte array deserializers
Activout.RestClient/Serialization/Implementation/StringDeserializer.cs, Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs
Updated Deserialize return type from Task<object> to Task<object?>. Removed #nullable disable directives.
Serialization manager implementation
Activout.RestClient/Serialization/Implementation/SerializationManager.cs
Constructor parameters changed to nullable (IReadOnlyCollection<ISerializer>? and IReadOnlyCollection<IDeserializer>?). GetSerializer and GetDeserializer return types changed to nullable. Existing null-checking logic preserved.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Homogeneous pattern: Consistent nullability signature updates across multiple files reduce cognitive overhead
  • Logic changes: XmlSerializer, StringSerializer, and FormUrlEncodedSerializer include null-handling logic that warrants verification
  • API surface: Verify nullable return types don't break downstream consumers expecting non-null defaults
  • SerializationManager: Constructor and method changes affect multiple call sites; ensure null propagation is handled correctly

Possibly related PRs

Poem

🐰 With question marks in tow, our types now bloom,
Nullable parameters light up the room!
Null-forgiving ops and safety checks galore,
The serializers dance—safer than before! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Nullable Serialization" directly and accurately reflects the primary objective of this pull request. The changeset systematically updates the serialization infrastructure across multiple files to support nullable reference types: making Serialize method parameters nullable (object? data), making Deserialize return types nullable (Task<object?>), updating ISerializationManager methods to return nullable types (ISerializer?, IDeserializer?), removing nullable-disable directives, and adding appropriate null handling logic. The title is concise, clear, and specific enough that a teammate scanning the repository history would understand this PR introduces nullable-aware serialization throughout the codebase.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nullable-serialization2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Activout.RestClient.Xml/XmlDeserializer.cs (1)

9-13: The null-coalescing operator defeats the nullable return type.

Line 13 uses ?? new object() which ensures this method never returns null, contradicting the Task<object?> return type. If deserialization can legitimately return null (e.g., for empty XML), remove the fallback and return the result directly. Otherwise, revert the return type to Task<object>.

Apply this diff if null results should be allowed:

-    public async Task<object?> Deserialize(HttpContent content, Type type)
-    {
-        var serializer = new System.Xml.Serialization.XmlSerializer(type);
-        await using var stream = await content.ReadAsStreamAsync();
-        return serializer.Deserialize(stream) ?? new object();
-    }
+    public async Task<object?> Deserialize(HttpContent content, Type type)
+    {
+        var serializer = new System.Xml.Serialization.XmlSerializer(type);
+        await using var stream = await content.ReadAsStreamAsync();
+        return serializer.Deserialize(stream);
+    }
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (2)

33-38: Guard against indexer properties to prevent reflection exceptions.

Indexers will throw on GetValue without index args. Filter them out and keep null‑safety.

Apply this diff:

-            var properties = type.GetProperties();
+            var properties = type.GetProperties()
+                .Where(p => p.CanRead && p.GetIndexParameters().Length == 0);
 
             return new FormUrlEncodedContent(
                 properties
-                    .Select(p => new { Property = p, Value = p.GetValue(data) })
+                    .Select(p => new { Property = p, Value = p.GetValue(data) })
                     .Where(x => x.Value != null)
                     .Select(x => new KeyValuePair<string, string>(GetKey(x.Property), SerializeValue(x.Value!)))
             );

18-39: Remove unused encoding parameter or use StringContent to honor the caller's encoding request.

The method accepts an encoding parameter but doesn't use it—FormUrlEncodedContent always uses UTF-8 by default and cannot be configured with a different encoding. If callers may need non-UTF-8 charsets, either (a) remove the encoding parameter, or (b) build the form body manually and pass it to StringContent instead, which allows specifying encoding.

🧹 Nitpick comments (11)
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (3)

18-23: Null input: return empty content, but avoid Enumerable.Empty allocation.

Use Array.Empty or collection expressions for zero‑alloc.

Apply this diff:

-                return new FormUrlEncodedContent(Enumerable.Empty<KeyValuePair<string, string>>());
+                return new FormUrlEncodedContent(Array.Empty<KeyValuePair<string, string>>());

46-49: Minor: expression-bodied + string.Empty.

Tiny polish for readability.

Apply this diff:

-        private static string SerializeValue(object value)
-        {
-            return value.ToString() ?? "";
-        }
+        private static string SerializeValue(object value) => value.ToString() ?? string.Empty;

11-14: Cache SupportedMediaTypes and adopt [] syntax.

Avoid new array per call and follow collection-expression guideline.

Example:

-        public IReadOnlyCollection<MediaType> SupportedMediaTypes => new[]
-        {
-            new MediaType("application/x-www-form-urlencoded")
-        };
+        private static readonly IReadOnlyCollection<MediaType> s_supported = 
+            new[] { new MediaType("application/x-www-form-urlencoded") };
+        public IReadOnlyCollection<MediaType> SupportedMediaTypes => s_supported;

If targeting C# 12:

private static readonly IReadOnlyCollection<MediaType> s_supported = [ new MediaType("application/x-www-form-urlencoded") ];

As per coding guidelines.

Activout.RestClient/Serialization/Implementation/StringSerializer.cs (3)

10-13: LGTM on nullable handling.

Matches ISerializer contract; safe ToString fallback.

You can make it expression‑bodied:

-        public HttpContent Serialize(object? data, Encoding encoding, MediaType mediaType)
-        {
-            return new StringContent(data?.ToString() ?? "", encoding, mediaType.Value);
-        }
+        public HttpContent Serialize(object? data, Encoding encoding, MediaType mediaType)
+            => new StringContent(data?.ToString() ?? string.Empty, encoding, mediaType.Value);

As per coding guidelines.


15-18: Case-insensitive media type check.

Be explicit about comparison.

-            return mediaType.Value.StartsWith("text/");
+            return mediaType.Value.StartsWith("text/", StringComparison.OrdinalIgnoreCase);

4-7: Prefer file‑scoped namespace.

Align with guideline.

-namespace Activout.RestClient.Serialization.Implementation
-{
+namespace Activout.RestClient.Serialization.Implementation;
-
-    internal class StringSerializer : ISerializer
-    {
+internal class StringSerializer : ISerializer
+{
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (3)

11-18: Use zero‑alloc empty array.

Replace new byte[0] with Array.Empty() (or [] if on C# 12).

-                null => new byte[0],
+                null => Array.Empty<byte>(),

Option (C# 12):

var bytes = data switch
{
    null => [],
    byte[] b => b,
    _ => encoding.GetBytes(data.ToString() ?? string.Empty)
};

As per coding guidelines.


25-28: Minor: expression‑bodied CanSerialize.

Small readability tweak.

-        public bool CanSerialize(MediaType mediaType)
-        {
-            return mediaType.Value == "application/octet-stream";
-        }
+        public bool CanSerialize(MediaType mediaType) => mediaType.Value == "application/octet-stream";

5-9: Prefer file‑scoped namespace.

Align with guideline.

-namespace Activout.RestClient.Serialization.Implementation
-{
+namespace Activout.RestClient.Serialization.Implementation;
-
-    internal class ByteArraySerializer : ISerializer
-    {
+internal class ByteArraySerializer : ISerializer
+{
Activout.RestClient/Serialization/Implementation/SerializationManager.cs (2)

10-24: Adopt collection expressions and/or cache immutable defaults.

Follow guideline and reduce ceremony/allocations.

Option A (keep ImmutableList):

-        public static readonly IReadOnlyCollection<ISerializer> DefaultSerializers = new List<ISerializer>
-            {
-                new FormUrlEncodedSerializer(),
-                new StringSerializer(),
-                new ByteArraySerializer()
-            }
-            .ToImmutableList();
+        public static readonly IReadOnlyCollection<ISerializer> DefaultSerializers =
+            [ new FormUrlEncodedSerializer(), new StringSerializer(), new ByteArraySerializer() ].ToImmutableList();
 
-        public static readonly IReadOnlyCollection<IDeserializer> DefaultDeserializers =
-            new List<IDeserializer>
-                {
-                    new StringDeserializer(),
-                    new ByteArrayDeserializer()
-                }
-                .ToImmutableList();
+        public static readonly IReadOnlyCollection<IDeserializer> DefaultDeserializers =
+            [ new StringDeserializer(), new ByteArrayDeserializer() ].ToImmutableList();

Option B (ImmutableArray for lighter weight):

public static readonly ImmutableArray<ISerializer> DefaultSerializers =
    [ new FormUrlEncodedSerializer(), new StringSerializer(), new ByteArraySerializer() ].ToImmutableArray();

public static readonly ImmutableArray<IDeserializer> DefaultDeserializers =
    [ new StringDeserializer(), new ByteArrayDeserializer() ].ToImmutableArray();

As per coding guidelines.


6-9: Prefer file‑scoped namespace.

Align with guideline.

-namespace Activout.RestClient.Serialization.Implementation
-{
-    public class SerializationManager : ISerializationManager
-    {
+namespace Activout.RestClient.Serialization.Implementation;
+
+public class SerializationManager : ISerializationManager
+{
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c4eb86 and 2d73cdf.

📒 Files selected for processing (13)
  • Activout.RestClient.Json/SystemTextJsonSerializer.cs (1 hunks)
  • Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1 hunks)
  • Activout.RestClient.Xml/XmlDeserializer.cs (1 hunks)
  • Activout.RestClient.Xml/XmlSerializer.cs (1 hunks)
  • Activout.RestClient/Serialization/IDeserializer.cs (1 hunks)
  • Activout.RestClient/Serialization/ISerializationManager.cs (1 hunks)
  • Activout.RestClient/Serialization/ISerializer.cs (1 hunks)
  • Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs (1 hunks)
  • Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (1 hunks)
  • Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (3 hunks)
  • Activout.RestClient/Serialization/Implementation/SerializationManager.cs (1 hunks)
  • Activout.RestClient/Serialization/Implementation/StringDeserializer.cs (1 hunks)
  • Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.cs: Use [] list syntax for collections
Use file-scoped namespaces
Use primary constructors wherever possible
Use records for data transfer objects (DTOs) and immutable data structures
Use var for local variable declarations when possible
Use expression-bodied members for simple methods and properties
Use pattern matching for type checks and deconstruction
Use using statements for resource management
Use async and await for asynchronous programming
Never include "Async" in method names
Only include comments that explain why something is done, not what is done

Files:

  • Activout.RestClient.Xml/XmlDeserializer.cs
  • Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs
  • Activout.RestClient/Serialization/ISerializer.cs
  • Activout.RestClient/Serialization/Implementation/StringSerializer.cs
  • Activout.RestClient/Serialization/IDeserializer.cs
  • Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs
  • Activout.RestClient/Serialization/Implementation/StringDeserializer.cs
  • Activout.RestClient.Json/SystemTextJsonSerializer.cs
  • Activout.RestClient/Serialization/Implementation/SerializationManager.cs
  • Activout.RestClient.Xml/XmlSerializer.cs
  • Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs
  • Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs
  • Activout.RestClient/Serialization/ISerializationManager.cs
🧬 Code graph analysis (13)
Activout.RestClient.Xml/XmlDeserializer.cs (3)
Activout.RestClient/Serialization/IDeserializer.cs (1)
  • Task (10-10)
Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs (1)
  • Task (18-34)
Activout.RestClient/Serialization/Implementation/StringDeserializer.cs (1)
  • Task (11-17)
Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs (4)
Activout.RestClient.Xml/XmlDeserializer.cs (1)
  • Task (9-14)
Activout.RestClient/Serialization/IDeserializer.cs (1)
  • Task (10-10)
Activout.RestClient/Serialization/Implementation/StringDeserializer.cs (1)
  • Task (11-17)
Activout.RestClient/Implementation/RequestHandler.cs (7)
  • Task (452-509)
  • Task (511-546)
  • Task (548-559)
  • Task (561-574)
  • HttpContent (427-434)
  • HttpContent (436-449)
  • Type (151-158)
Activout.RestClient/Serialization/ISerializer.cs (1)
Activout.RestClient/MediaType.cs (2)
  • MediaType (8-11)
  • MediaType (15-22)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (7)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)
  • HttpContent (32-37)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1)
  • HttpContent (20-25)
Activout.RestClient.Xml/XmlSerializer.cs (1)
  • HttpContent (13-27)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (1)
  • HttpContent (11-23)
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (1)
  • HttpContent (18-39)
Activout.RestClient/Implementation/RequestHandler.cs (2)
  • HttpContent (427-434)
  • HttpContent (436-449)
Activout.RestClient/Serialization/IDeserializer.cs (4)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)
  • HttpContent (32-37)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (1)
  • HttpContent (11-23)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1)
  • HttpContent (10-13)
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (7)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)
  • HttpContent (32-37)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1)
  • HttpContent (20-25)
Activout.RestClient.Xml/XmlSerializer.cs (1)
  • HttpContent (13-27)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (1)
  • HttpContent (18-39)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1)
  • HttpContent (10-13)
Activout.RestClient/Implementation/RequestHandler.cs (2)
  • HttpContent (427-434)
  • HttpContent (436-449)
Activout.RestClient/Serialization/Implementation/StringDeserializer.cs (3)
Activout.RestClient.Xml/XmlDeserializer.cs (1)
  • Task (9-14)
Activout.RestClient/Serialization/IDeserializer.cs (1)
  • Task (10-10)
Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs (1)
  • Task (18-34)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (3)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1)
  • HttpContent (20-25)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1)
  • HttpContent (10-13)
Activout.RestClient/Serialization/Implementation/SerializationManager.cs (6)
Activout.RestClient/Serialization/ISerializationManager.cs (2)
  • ISerializer (5-5)
  • IDeserializer (6-6)
Activout.RestClient/MediaType.cs (2)
  • MediaType (8-11)
  • MediaType (15-22)
Activout.RestClient/Serialization/IDeserializer.cs (1)
  • CanDeserialize (11-11)
Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs (1)
  • CanDeserialize (36-39)
Activout.RestClient/Serialization/Implementation/StringDeserializer.cs (1)
  • CanDeserialize (19-22)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonDeserializer.cs (1)
  • CanDeserialize (35-38)
Activout.RestClient.Xml/XmlSerializer.cs (6)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)
  • HttpContent (32-37)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1)
  • HttpContent (20-25)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (1)
  • HttpContent (11-23)
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (1)
  • HttpContent (18-39)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1)
  • HttpContent (10-13)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (6)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)
  • HttpContent (32-37)
Activout.RestClient.Xml/XmlSerializer.cs (1)
  • HttpContent (13-27)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (1)
  • HttpContent (11-23)
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (1)
  • HttpContent (18-39)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1)
  • HttpContent (10-13)
Activout.RestClient/Serialization/Implementation/FormUrlEncodedSerializer.cs (7)
Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)
  • HttpContent (32-37)
Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1)
  • HttpContent (20-25)
Activout.RestClient.Xml/XmlSerializer.cs (1)
  • HttpContent (13-27)
Activout.RestClient/Serialization/ISerializer.cs (1)
  • HttpContent (9-9)
Activout.RestClient/Serialization/Implementation/ByteArraySerializer.cs (1)
  • HttpContent (11-23)
Activout.RestClient/Serialization/Implementation/StringSerializer.cs (1)
  • HttpContent (10-13)
Activout.RestClient/Implementation/RequestHandler.cs (2)
  • HttpContent (427-434)
  • HttpContent (436-449)
Activout.RestClient/Serialization/ISerializationManager.cs (2)
Activout.RestClient/Implementation/RestClient.cs (2)
  • RestClient (12-75)
  • RestClient (20-26)
Activout.RestClient/MediaType.cs (2)
  • MediaType (8-11)
  • MediaType (15-22)
🔇 Additional comments (10)
Activout.RestClient/Serialization/IDeserializer.cs (1)

10-10: LGTM! Nullable return type properly reflects deserialization semantics.

The change from Task<object> to Task<object?> correctly acknowledges that deserialization may yield null results.

Activout.RestClient.Json/SystemTextJsonSerializer.cs (1)

32-36: LGTM! System.Text.Json handles null inputs gracefully.

The nullable parameter is correctly handled by JsonSerializer.Serialize, which will serialize null as the JSON literal "null".

Activout.RestClient/Serialization/Implementation/ByteArrayDeserializer.cs (1)

18-33: LGTM! Nullable return type correctly handles empty byte arrays and instantiation.

The implementation properly returns null for empty byte arrays (line 33) and the nullable return type accounts for Activator.CreateInstance potentially returning null.

Activout.RestClient/Serialization/Implementation/StringDeserializer.cs (1)

11-16: LGTM! Nullable return type accounts for potential null from Activator.

The implementation correctly handles the string case and the nullable return type appropriately reflects that Activator.CreateInstance may return null for reference types.

Activout.RestClient/Serialization/ISerializer.cs (1)

9-9: LGTM! Nullable parameter enables serializers to handle null inputs.

The change from object data to object? data correctly allows null to be passed, with implementations responsible for appropriate handling.

Activout.RestClient.Newtonsoft.Json/NewtonsoftJsonSerializer.cs (1)

20-24: LGTM! Newtonsoft.Json handles null inputs gracefully.

The nullable parameter is correctly handled by JsonConvert.SerializeObject, which will serialize null as the JSON literal "null".

Activout.RestClient.Xml/XmlSerializer.cs (1)

13-18: LGTM! Null check properly handles nullable input.

The null guard returns appropriate empty content, and the non-null path safely uses data.GetType() without risk of null reference.

Activout.RestClient/Serialization/ISerializationManager.cs (1)

1-1: Good move to file‑scoped namespace.

Style aligns with guideline.

Activout.RestClient/Serialization/Implementation/SerializationManager.cs (2)

30-35: LGTM on nullable constructor parameters.

Keeps defaults while enabling NRTs; ordering preserved.


37-47: LGTM on nullable returns with guard.

Nullables reflect “not found” semantics; argument validation retained.

Comment on lines +5 to +6
ISerializer? GetSerializer(MediaType mediaType);
IDeserializer? GetDeserializer(MediaType mediaType);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Update call sites to accept nullable serializers/deserializers.

GetSerializer/GetDeserializer now return nullable; ensure consumers don’t pass possibly‑null into non‑nullable parameters or assign to non‑nullable fields.

Run to find risky sites:

If RequestHandler.GetHttpContent takes ISerializer (non‑nullable), change it to accept ISerializer? and keep the null guard:

-private static HttpContent GetHttpContent(ISerializer serializer, object value, MediaType contentType)
+private static HttpContent GetHttpContent(ISerializer? serializer, object value, MediaType contentType)

As per coding guidelines.


🏁 Script executed:

#!/bin/bash
# Find usages of GetSerializer/GetDeserializer and nearby null checks/assignments.
rg -n -C3 '\bGetSerializer\s*\('
rg -n -C3 '\bGetDeserializer\s*\('

# Look for methods that accept ISerializer (non-null) but may receive nullable.
rg -n -C3 -P 'GetHttpContent\s*\(\s*ISerializer\s' --type=cs

Length of output: 6170


I need to verify the field declarations and check for null guards around the usage points.

[/run_scripts]


🏁 Script executed:

#!/bin/bash
# Check field declarations in RestClient.cs
rg -n -B2 -A2 '_context\.DefaultSerializer\s*=' --type=cs

# Check field declarations in RequestHandler.cs
rg -n -B2 -A2 '_serializer\s*=' --type=cs

# Check for null guards near line 432-433 in RequestHandler.cs
sed -n '425,445p' Activout.RestClient/Implementation/RequestHandler.cs

# Check field type declarations
rg -n 'ISerializer\s+_serializer' --type=cs
rg -n 'DefaultSerializer' --type=cs -B1 -A1

Length of output: 4572


Update call sites and field declarations to handle nullable serializers/deserializers.

GetSerializer/GetDeserializer now return nullable. Three locations require updates:

  1. RestClientContext.cs line 34: Change ISerializer DefaultSerializer to ISerializer? DefaultSerializer
  2. RequestHandler.cs line 37: Change private readonly ISerializer _serializer to private readonly ISerializer? _serializer
  3. RequestHandler.cs line 436: Change GetHttpContent(ISerializer serializer, ...) to GetHttpContent(ISerializer? serializer, ...) — the existing null check at line 443 confirms nullable values are possible.

Also verify all usages of these fields/return values handle null appropriately (e.g., RequestHandler.cs line 101 assigns GetSerializer directly to _serializer without null checks; ensure null handling is consistent).

🤖 Prompt for AI Agents
In Activout.RestClient/Serialization/ISerializationManager.cs around lines 5-6
the GetSerializer/GetDeserializer signatures were changed to return nullable;
update the consuming code: in RestClientContext.cs line 34 change the
field/property type to ISerializer? DefaultSerializer; in RequestHandler.cs
change the private readonly ISerializer _serializer at line 37 to ISerializer?
_serializer and change the method signature at line 436 from
GetHttpContent(ISerializer serializer, ...) to GetHttpContent(ISerializer?
serializer, ...); then audit and fix usages where serializers/deserializers are
consumed (for example RequestHandler.cs line 101 where you assign GetSerializer
result directly) by adding null checks/fallbacks or conditional logic to avoid
dereferencing null (or throw a clear exception where appropriate) so all code
paths handle nullable values consistently.

@twogood twogood enabled auto-merge (rebase) October 25, 2025 08:36
@twogood twogood merged commit 6663fdc into main Oct 25, 2025
9 checks passed
@twogood twogood deleted the nullable-serialization2 branch October 25, 2025 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant