1
- using Newtonsoft . Json ;
1
+ // Copyright (c) 2024 navidata.io Corp
2
+ // LICENSE-SPDX: <LGPL-3.0-only>
3
+
4
+ using Newtonsoft . Json ;
2
5
using Newtonsoft . Json . Linq ;
6
+ using System . Text ;
3
7
4
8
namespace navidataIO . Utils . Json ;
5
9
@@ -46,11 +50,10 @@ public static JObject InsertTokenAsString<T>(this JObject parent, string propert
46
50
/// Attempts to convert the specified JSON property to a value of type <typeparamref name="T"/>.
47
51
/// Returns the converted value, or the default value if the property does not exist or cannot be converted.
48
52
/// </summary>
49
- public static T ? ReadPropertySafe < T > ( this JObject json , string property , T ? defaultValue = default , ILogger ? logger = default )
53
+ public static T ? ReadPropertySafe < T > ( this JObject json , string property , T ? defaultValue = default , ILogger ? logger = null )
50
54
{
51
55
if ( json == null ) throw new ArgumentNullException ( nameof ( json ) ) ;
52
- if ( string . IsNullOrEmpty ( property ) )
53
- throw new ArgumentException ( "Value cannot be null or empty." , nameof ( property ) ) ;
56
+ if ( string . IsNullOrEmpty ( property ) ) throw new ArgumentException ( "Value cannot be null or empty." , nameof ( property ) ) ;
54
57
55
58
if ( json . TryGetValue ( property , StringComparison . InvariantCultureIgnoreCase , out var value ) )
56
59
{
@@ -118,11 +121,11 @@ public static bool TryParseJson<T>(this string json, out T? result) where T : cl
118
121
try
119
122
{
120
123
result = JsonConvert . DeserializeObject < T > ( json ) ;
121
- return result != default ;
124
+ return result != null ;
122
125
}
123
126
catch
124
127
{
125
- result = default ;
128
+ result = null ;
126
129
return false ;
127
130
}
128
131
}
@@ -139,7 +142,7 @@ public static bool TryParseJsonObject(this string json, out JObject? result)
139
142
}
140
143
catch
141
144
{
142
- result = default ;
145
+ result = null ;
143
146
return false ;
144
147
}
145
148
}
@@ -156,17 +159,204 @@ public static bool TryParseJsonArray(this string json, out JArray? result)
156
159
}
157
160
catch
158
161
{
159
- result = default ;
162
+ result = null ;
160
163
return false ;
161
164
}
162
165
}
163
166
164
167
/// <summary>
165
168
/// Serializes the object to a JSON string, with specified formatting.
166
169
/// </summary>
170
+ [ Obsolete ( "Use AsJson<T>(T?, Formatting) instead." ) ]
167
171
public static string ToJsonString < T > ( this T ? value , Newtonsoft . Json . Formatting format = Newtonsoft . Json . Formatting . Indented ) where T : class =>
168
- value == default
172
+ value == null
169
173
? ""
170
174
: JsonConvert . SerializeObject ( value , new JsonSerializerSettings { Formatting = format , NullValueHandling = NullValueHandling . Ignore } ) ;
171
175
176
+ /// <summary>
177
+ /// Creates or overwrites the named property on a <see cref="JObject"/>, but only if the provided value is not <c>null</c>.
178
+ /// </summary>
179
+ public static JObject WithOptional ( this JObject json , string propertyName , JToken ? value )
180
+ {
181
+ if ( json == null ) throw new ArgumentNullException ( nameof ( json ) ) ;
182
+ if ( propertyName == null ) throw new ArgumentNullException ( nameof ( propertyName ) ) ;
183
+
184
+ if ( value is null || value is { Type : JTokenType . Null } ) return json ;
185
+ if ( value is JValue { Value : null } /* default(string) */ ) return json ;
186
+
187
+ json [ propertyName ] = value ;
188
+ return json ;
189
+ }
190
+
191
+ /// <summary>
192
+ /// Creates or overwrites the named property on a <see cref="JObject"/>, but only if the provided value is not <c>null</c>
193
+ /// and the provided factory method produces a non-null <see cref="JToken"/>.
194
+ /// </summary>
195
+ public static JObject WithOptional < T > ( this JObject json , string propertyName , T ? value , Func < T , JToken ? > createToken )
196
+ {
197
+ if ( json == null ) throw new ArgumentNullException ( nameof ( json ) ) ;
198
+ if ( propertyName == null ) throw new ArgumentNullException ( nameof ( propertyName ) ) ;
199
+
200
+ if ( value is null ) return json ;
201
+
202
+ if ( createToken ( value ) is { } token )
203
+ json [ propertyName ] = token ;
204
+ return json ;
205
+ }
206
+
207
+ /// <summary>
208
+ /// Creates or overwrites the named JSON property as a new <see cref="JArray"/>,
209
+ /// generated from items of the provides collection using the provided <paramref name="createToken"/> factory method,
210
+ /// but only if the provided collection is not <c>null</c>. Any items producing a <c>null</c> token are ignored.
211
+ /// </summary>
212
+ public static JObject WithOptionalArray < T > ( this JObject parent , string propertyName ,
213
+ ICollection < T > ? collection , Func < T , JToken ? > createToken )
214
+ {
215
+ if ( parent == null ) throw new ArgumentNullException ( nameof ( parent ) ) ;
216
+ if ( propertyName == null ) throw new ArgumentNullException ( nameof ( propertyName ) ) ;
217
+
218
+ if ( collection is null ) return parent ;
219
+
220
+ parent [ propertyName ] = new JArray ( collection . Select ( createToken ) . WhereNotNull ( ) ) ;
221
+ return parent ;
222
+ }
223
+
224
+ /// <summary>
225
+ /// Creates or overwrites the named property on a <see cref="JObject"/>, regardless of the provided value.
226
+ /// </summary>
227
+ public static JObject WithProperty ( this JObject json , string propertyName , JToken ? value )
228
+ {
229
+ if ( json == null ) throw new ArgumentNullException ( nameof ( json ) ) ;
230
+ if ( propertyName == null ) throw new ArgumentNullException ( nameof ( propertyName ) ) ;
231
+ json [ propertyName ] = value ;
232
+ return json ;
233
+ }
234
+
235
+ /// <summary>
236
+ /// Selects a <see cref="JObject"/> from the provided JSON at the specified path.
237
+ /// Throws an <see cref="InvalidOperationException"/> if the token is not a <see cref="JObject"/> or does not exist.
238
+ /// </summary>
239
+ public static JObject RequireObject ( this JObject json , string jsonPath )
240
+ {
241
+ if ( json == null ) throw new ArgumentNullException ( nameof ( json ) ) ;
242
+ return json . SelectObject ( jsonPath , throwIfNull : true ) ! ;
243
+ }
244
+
245
+ /// <summary>
246
+ /// Selects a <see cref="JObject"/> from the provided JSON at the specified path.
247
+ /// Returns <c>null</c> if the token does not exist, unless <paramref name="throwIfNull"/> is <c>true</c>,
248
+ /// in which case an <see cref="InvalidOperationException"/> is thrown.
249
+ /// Always throws an <see cref="InvalidOperationException"/> if the token is not a <see cref="JObject"/>.
250
+ /// </summary>
251
+ public static JObject ? SelectObject ( this JObject json , string jsonPath , bool throwIfNull = false ) =>
252
+ ( json ?? throw new ArgumentNullException ( nameof ( json ) ) ) . SelectToken ( jsonPath ) switch
253
+ {
254
+ JObject jObject => jObject ,
255
+ null when throwIfNull => throw new InvalidOperationException ( $ "Expected token at path { jsonPath } does not exist and 'throwIfNull' was selected.") ,
256
+ null => null ,
257
+ _ => throw new InvalidOperationException ( $ "Unable to select object from JSON at path: { jsonPath } ")
258
+ } ;
259
+
260
+ /// <summary>
261
+ /// Renames a property on a <see cref="JObject"/>. If the property does not exist, it is ignored.
262
+ /// </summary>
263
+ public static JObject RenameProperty ( this JObject parent , string oldName , string newName )
264
+ {
265
+ if ( parent [ oldName ] is { } existingToken )
266
+ {
267
+ parent [ newName ] = existingToken ;
268
+ parent . Remove ( oldName ) ;
269
+ }
270
+ return parent ;
271
+ }
272
+
273
+ /// <summary>
274
+ /// Removes the specified properties from a <see cref="JObject"/>. If a property does not exist, it is ignored.
275
+ /// </summary>
276
+ public static JObject RemoveProperties ( this JObject obj , params string [ ] propertyNames )
277
+ {
278
+ if ( obj == null ) throw new ArgumentNullException ( nameof ( obj ) ) ;
279
+
280
+ foreach ( var propertyName in propertyNames )
281
+ {
282
+ obj . Remove ( propertyName ) ;
283
+ }
284
+ return obj ;
285
+ }
286
+
287
+ /// <summary>
288
+ /// Serializes the object to a JSON string.
289
+ /// </summary>
290
+ /// <param name="obj">The object to serialize.</param>
291
+ /// <param name="formatting">The (optional) <see cref="Formatting"/> to apply when serializing. If omitted, and no <paramref name="serializerSettings"/> are provided, <see cref="Formatting.Indented"/> is used. If set, and <paramref name="serializerSettings"/> are provided, this argument overwrites <see cref="JsonSerializerSettings.Formatting"/>.</param>
292
+ /// <param name="serializerSettings">The (optional) <see cref="JsonSerializerSettings"/> to use when serializing.</param>
293
+ /// <param name="resultIfNull">The value to return if <paramref name="obj"/> is null (default is <c>null</c>).</param>
294
+ /// <returns>The serialized object as a string.</returns>
295
+ public static string ? AsJson < T > ( this T ? obj , Formatting ? formatting = null ,
296
+ JsonSerializerSettings ? serializerSettings = null , string ? resultIfNull = null )
297
+ {
298
+ if ( obj is null ) return resultIfNull ;
299
+
300
+ var settings = serializerSettings ?? new JsonSerializerSettings ( ) ;
301
+ if ( serializerSettings is null && formatting is null )
302
+ settings . Formatting = Formatting . Indented ;
303
+ else if ( formatting is not null )
304
+ settings . Formatting = formatting . Value ;
305
+
306
+ return obj . AsJson ( JsonSerializer . Create ( settings ) ) ;
307
+ }
308
+
309
+ /// <summary>
310
+ /// Serializes the object to a JSON string.
311
+ /// </summary>
312
+ /// <param name="obj">The object to serialize.</param>
313
+ /// <param name="serializer">The <see cref="JsonSerializer"/> to use when serializing.</param>
314
+ /// <param name="resultIfNull">The value to return if <paramref name="obj"/> is null (default is <c>null</c>).</param>
315
+ /// <returns>The serialized object as a string.</returns>
316
+ public static string ? AsJson < T > ( this T ? obj , JsonSerializer serializer , string ? resultIfNull = null )
317
+ {
318
+ if ( obj is null ) return resultIfNull ;
319
+ if ( serializer == null ) throw new ArgumentNullException ( nameof ( serializer ) ) ;
320
+
321
+ var sb = new StringBuilder ( ) ;
322
+ using ( var writer = new StringWriter ( sb ) )
323
+ {
324
+ serializer . Serialize ( writer , obj ) ;
325
+ }
326
+ return sb . ToString ( ) ;
327
+ }
328
+
329
+
330
+ /// <summary>
331
+ /// Creates a <see cref="JToken"/> from the provided object using the specified <see cref="JsonSerializer"/>.
332
+ /// Returns <c>null</c> if the object is <c>null</c>.
333
+ /// </summary>
334
+ /// <param name="obj">The object to convert to JSON.</param>
335
+ /// <param name="formatting">The (optional) <see cref="Formatting"/> to apply when serializing. If omitted, and no <paramref name="serializerSettings"/> are provided, <see cref="Formatting.Indented"/> is used. If set, and <paramref name="serializerSettings"/> are provided, this argument overwrites <see cref="JsonSerializerSettings.Formatting"/>.</param>
336
+ /// <param name="serializerSettings">The (optional) <see cref="JsonSerializerSettings"/> to use when serializing.</param>
337
+ public static JToken ? ToJson < T > ( this T obj , Formatting ? formatting = null ,
338
+ JsonSerializerSettings ? serializerSettings = null )
339
+ {
340
+ var settings = serializerSettings ?? new JsonSerializerSettings ( ) ;
341
+ if ( serializerSettings is null && formatting is null )
342
+ settings . Formatting = Formatting . Indented ;
343
+ else if ( formatting is not null )
344
+ settings . Formatting = formatting . Value ;
345
+ var serializer = JsonSerializer . Create ( settings ) ;
346
+
347
+ return obj . ToJson ( serializer ) ;
348
+ }
349
+
350
+ /// <summary>
351
+ /// Creates a <see cref="JToken"/> from the provided object using the specified <see cref="JsonSerializer"/>.
352
+ /// Returns <c>null</c> if the object is <c>null</c>.
353
+ /// </summary>
354
+ public static JToken ? ToJson < T > ( this T obj , JsonSerializer serializer )
355
+ {
356
+ object ? o = obj ;
357
+ return o is null
358
+ ? null
359
+ : JToken . FromObject ( o , serializer ?? throw new ArgumentNullException ( nameof ( serializer ) ) ) ;
360
+ }
361
+
172
362
}
0 commit comments