Add IParsable<T> fallback converter for automatic scalar support#135
Add IParsable<T> fallback converter for automatic scalar support#135fdcastel wants to merge 1 commit into
Conversation
Types implementing IParsable<T> (introduced in .NET 7) are now automatically deserialized from YAML scalars using T.Parse(value, InvariantCulture). For serialization, IFormattable.ToString(null, InvariantCulture) is used when available, with a fallback to ToString(). This provides out-of-the-box support for IPAddress and any user-defined IParsable<T> types without requiring custom converters. The fallback is lower priority than all other built-in converters, ensuring that types with dedicated converters (string, int, DateTime, etc.) continue to use their optimized paths.
|
@xoofx I’m not certain this is the right direction, so I’m sharing it as an RFC. If you can, I would appreciate your guidance. |
|
So, I'm not sure on this one. As you pointed out many BCL types don't support it, so the interest is quite limited. For serialization, it might require forcing quotes as the string might conflict with YAML syntax.
In any case, if such feature was introduced, it would need to support source generator and detect this at compile time. |
|
Got it. 👍🏻 I’m still having trouble finding a sensible way to handle my custom converters and source generators, but I’m optimistic I’ll arrive at a good solution. |
|
Nah! Closing this one. |
I can release a package or wait for some other changes. Let me know. |
|
3.7.0 should be up! |
Thanks man! Much appreciated! |
Motivation
Rather than adding individual converters for every BCL type that can be represented as a string (e.g.,
IPAddress,IPEndPoint,Version), SharpYaml can leverage theIParsable<T>interface (introduced in .NET 7) as a generic fallback for scalar-to-object conversion.Currently, types like
System.Net.IPAddressthat appear as YAML scalars require custom converters. This is a common source of friction for configuration-heavy applications.Solution
A new
YamlParsableConverterFactory+YamlParsableConverter<T>pair is added as a fallback in the built-in converter resolution chain, positioned just before the finalYamlObjectConverter<T>:IParsable<T>, callsT.TryParse(scalarValue, CultureInfo.InvariantCulture, out result)IFormattable.ToString(null, CultureInfo.InvariantCulture)when available, falling back toToString()int,DateTime,Guid, etc.) continue using their optimized paths#if NET7_0_OR_GREATER)Types automatically supported
This immediately enables scalar support for:
System.Net.IPAddress(most impactful for config scenarios)IParsable<T>typesNote:
IPEndPoint,Version, andUrido not implementIParsable<T>in current .NET and would need dedicated converters if needed.Caveats
IParsable<T>detection to emit the same pattern.[UnconditionalSuppressMessage]for trimming/AOT since it's only reachable through the reflection-based converter resolution path.Tests
10 tests covering:
IPAddressroundtrip (IPv4 and IPv6)IPAddressproperties (deserialize + roundtrip)IParsable<T>struct (Temperature) with roundtripYamlExceptionint) take priority over IParsableIPAddress?properties (with value and null)