1+ using System ;
2+ using System . Text . Json ;
3+ using System . Text . Json . Serialization ;
4+
5+ namespace Evoq . Blockchain ;
6+
7+ /// <summary>
8+ /// JSON converter for the <see cref="Hex"/> type that enables automatic serialization and deserialization.
9+ /// </summary>
10+ /// <remarks>
11+ /// <para>
12+ /// This converter enables <see cref="Hex"/> values to be seamlessly serialized to and from JSON as hex strings.
13+ /// During serialization, <see cref="Hex"/> values are converted to their string representation (e.g., "0x1234abcd").
14+ /// During deserialization, hex strings are parsed back into <see cref="Hex"/> values.
15+ /// </para>
16+ ///
17+ /// <para>
18+ /// <strong>Usage:</strong> Register this converter with your <see cref="JsonSerializerOptions"/>:
19+ /// </para>
20+ ///
21+ /// <code>
22+ /// var options = new JsonSerializerOptions
23+ /// {
24+ /// Converters = { new HexJsonConverter() }
25+ /// };
26+ ///
27+ /// // Now Hex properties in your DTOs will be automatically serialized/deserialized
28+ /// string json = JsonSerializer.Serialize(myObject, options);
29+ /// var result = JsonSerializer.Deserialize<MyType>(json, options);
30+ /// </code>
31+ ///
32+ /// <para>
33+ /// <strong>Supported Formats:</strong> The converter accepts hex strings with or without the "0x" prefix during
34+ /// deserialization, but always serializes with the "0x" prefix for consistency.
35+ /// </para>
36+ ///
37+ /// <para>
38+ /// <strong>Error Handling:</strong> Invalid hex strings during deserialization will throw a <see cref="JsonException"/>
39+ /// with a descriptive error message. This includes strings with invalid hex characters or odd-length hex strings
40+ /// (unless using lenient parsing options).
41+ /// </para>
42+ /// </remarks>
43+ public class HexJsonConverter : JsonConverter < Hex >
44+ {
45+ /// <summary>
46+ /// Reads a JSON token and converts it to a <see cref="Hex"/> value.
47+ /// </summary>
48+ /// <param name="reader">The JSON reader to read from.</param>
49+ /// <param name="typeToConvert">The type being converted (should be <see cref="Hex"/>).</param>
50+ /// <param name="options">The JSON serializer options.</param>
51+ /// <returns>A <see cref="Hex"/> value parsed from the JSON string.</returns>
52+ /// <exception cref="JsonException">Thrown when the JSON token is not a string or when the hex string is invalid.</exception>
53+ public override Hex Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
54+ {
55+ if ( reader . TokenType != JsonTokenType . String )
56+ {
57+ throw new JsonException ( $ "Expected string token for Hex deserialization, but got { reader . TokenType } ") ;
58+ }
59+
60+ string ? hexString = reader . GetString ( ) ;
61+
62+ if ( hexString == null )
63+ {
64+ throw new JsonException ( "Cannot deserialize null string to Hex" ) ;
65+ }
66+
67+ try
68+ {
69+ return Hex . Parse ( hexString ) ;
70+ }
71+ catch ( ArgumentException ex )
72+ {
73+ throw new JsonException ( $ "Invalid hex string '{ hexString } ': { ex . Message } ", ex ) ;
74+ }
75+ catch ( FormatException ex )
76+ {
77+ throw new JsonException ( $ "Invalid hex string format '{ hexString } ': { ex . Message } ", ex ) ;
78+ }
79+ }
80+
81+ /// <summary>
82+ /// Writes a <see cref="Hex"/> value to JSON as a hex string.
83+ /// </summary>
84+ /// <param name="writer">The JSON writer to write to.</param>
85+ /// <param name="value">The <see cref="Hex"/> value to serialize.</param>
86+ /// <param name="options">The JSON serializer options.</param>
87+ public override void Write ( Utf8JsonWriter writer , Hex value , JsonSerializerOptions options )
88+ {
89+ writer . WriteStringValue ( value . ToString ( ) ) ;
90+ }
91+ }
92+
93+ /// <summary>
94+ /// JSON converter for nullable <see cref="Hex"/> types that enables automatic serialization and deserialization.
95+ /// </summary>
96+ /// <remarks>
97+ /// <para>
98+ /// This converter handles nullable <see cref="Hex"/> properties in DTOs, serializing null values as JSON null
99+ /// and deserializing JSON null back to null <see cref="Hex"/> values.
100+ /// </para>
101+ ///
102+ /// <para>
103+ /// <strong>Usage:</strong> This converter is typically registered alongside <see cref="HexJsonConverter"/>:
104+ /// </para>
105+ ///
106+ /// <code>
107+ /// var options = new JsonSerializerOptions
108+ /// {
109+ /// Converters = {
110+ /// new HexJsonConverter(),
111+ /// new NullableHexJsonConverter()
112+ /// }
113+ /// };
114+ /// </code>
115+ ///
116+ /// <para>
117+ /// <strong>Note:</strong> In most cases, you only need to register <see cref="HexJsonConverter"/> as System.Text.Json
118+ /// will automatically handle nullable conversions. This converter is provided for explicit nullable handling scenarios.
119+ /// </para>
120+ /// </remarks>
121+ public class NullableHexJsonConverter : JsonConverter < Hex ? >
122+ {
123+ /// <summary>
124+ /// Reads a JSON token and converts it to a nullable <see cref="Hex"/> value.
125+ /// </summary>
126+ /// <param name="reader">The JSON reader to read from.</param>
127+ /// <param name="typeToConvert">The type being converted (should be <see cref="Hex"/>?).</param>
128+ /// <param name="options">The JSON serializer options.</param>
129+ /// <returns>A nullable <see cref="Hex"/> value parsed from the JSON, or null if the JSON token is null.</returns>
130+ /// <exception cref="JsonException">Thrown when the JSON token is not a string or null, or when the hex string is invalid.</exception>
131+ public override Hex ? Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
132+ {
133+ if ( reader . TokenType == JsonTokenType . Null )
134+ {
135+ return null ;
136+ }
137+
138+ if ( reader . TokenType != JsonTokenType . String )
139+ {
140+ throw new JsonException ( $ "Expected string or null token for nullable Hex deserialization, but got { reader . TokenType } ") ;
141+ }
142+
143+ string ? hexString = reader . GetString ( ) ;
144+
145+ if ( hexString == null )
146+ {
147+ return null ;
148+ }
149+
150+ try
151+ {
152+ return Hex . Parse ( hexString ) ;
153+ }
154+ catch ( ArgumentException ex )
155+ {
156+ throw new JsonException ( $ "Invalid hex string '{ hexString } ': { ex . Message } ", ex ) ;
157+ }
158+ catch ( FormatException ex )
159+ {
160+ throw new JsonException ( $ "Invalid hex string format '{ hexString } ': { ex . Message } ", ex ) ;
161+ }
162+ }
163+
164+ /// <summary>
165+ /// Writes a nullable <see cref="Hex"/> value to JSON.
166+ /// </summary>
167+ /// <param name="writer">The JSON writer to write to.</param>
168+ /// <param name="value">The nullable <see cref="Hex"/> value to serialize.</param>
169+ /// <param name="options">The JSON serializer options.</param>
170+ public override void Write ( Utf8JsonWriter writer , Hex ? value , JsonSerializerOptions options )
171+ {
172+ if ( value . HasValue )
173+ {
174+ writer . WriteStringValue ( value . Value . ToString ( ) ) ;
175+ }
176+ else
177+ {
178+ writer . WriteNullValue ( ) ;
179+ }
180+ }
181+ }
0 commit comments