Skip to content

Commit 379debb

Browse files
committed
Support proper (de-)serialization of unions with 3 or more variants
1 parent c0af45a commit 379debb

2 files changed

Lines changed: 224 additions & 69 deletions

File tree

src/Elastic.Clients.Elasticsearch/_Shared/Next/JsonReaderExtensions.cs

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,12 +475,153 @@ public static KeyValuePair<TKey, TValue> ReadKeyValuePairValue<TKey, TValue>(thi
475475

476476
return selector(ref reader, options) switch
477477
{
478-
UnionTag.T1 => (readType1 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T1>(ref r, o))).Invoke(ref reader, options),
479-
UnionTag.T2 => (readType2 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T2>(ref r, o))).Invoke(ref reader, options),
478+
1 => (readType1 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T1>(ref r, o))).Invoke(ref reader, options),
479+
2 => (readType2 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T2>(ref r, o))).Invoke(ref reader, options),
480480
_ => throw new InvalidOperationException($"Failed to select an union variant for union of type '{typeof(T1).Name}' or '{typeof(T2).Name}'.")
481481
};
482482
}
483483

484+
/// <summary>
485+
/// Reads an inline union value from a given <see cref="Utf8JsonReader"/> instance.
486+
/// </summary>
487+
/// <typeparam name="T1">The first union type.</typeparam>
488+
/// <typeparam name="T2">The second union type.</typeparam>
489+
/// <typeparam name="T3">The third union type.</typeparam>
490+
/// <param name="reader">A reference to the <see cref="Utf8JsonReader"/>.</param>
491+
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
492+
/// <param name="selector">A function that selects the union variant (e.g. based on the current JSON token type).</param>
493+
/// <param name="readType1">
494+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the first union variant type, or <see langword="null"/>
495+
/// to use the default converter for the type <typeparamref name="T1"/>.
496+
/// </param>
497+
/// <param name="readType2">
498+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the second union variant type, or <see langword="null"/>
499+
/// to use the default converter for the type <typeparamref name="T2"/>.
500+
/// </param>
501+
/// <param name="readType3">
502+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the third union variant type, or <see langword="null"/>
503+
/// to use the default converter for the type <typeparamref name="T3"/>.
504+
/// </param>
505+
/// <returns>A boxed value of the selected union variant.</returns>
506+
/// <exception cref="InvalidOperationException">If no matching union variant could be selected.</exception>
507+
public static object? ReadUnionValue<T1, T2, T3>(this ref Utf8JsonReader reader, JsonSerializerOptions options,
508+
JsonUnionSelectorFunc selector, JsonReadFunc<T1>? readType1, JsonReadFunc<T2>? readType2,
509+
JsonReadFunc<T3>? readType3)
510+
{
511+
if (reader.TokenType is JsonTokenType.Null)
512+
{
513+
return null;
514+
}
515+
516+
return selector(ref reader, options) switch
517+
{
518+
1 => (readType1 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T1>(ref r, o))).Invoke(ref reader, options),
519+
2 => (readType2 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T2>(ref r, o))).Invoke(ref reader, options),
520+
3 => (readType3 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T3>(ref r, o))).Invoke(ref reader, options),
521+
_ => throw new InvalidOperationException($"Failed to select a union variant for union of type '{typeof(T1).Name}', '{typeof(T2).Name}' or '{typeof(T3).Name}'.")
522+
};
523+
}
524+
525+
/// <summary>
526+
/// Reads an inline union value from a given <see cref="Utf8JsonReader"/> instance.
527+
/// </summary>
528+
/// <typeparam name="T1">The first union type.</typeparam>
529+
/// <typeparam name="T2">The second union type.</typeparam>
530+
/// <typeparam name="T3">The third union type.</typeparam>
531+
/// <typeparam name="T4">The fourth union type.</typeparam>
532+
/// <param name="reader">A reference to the <see cref="Utf8JsonReader"/>.</param>
533+
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
534+
/// <param name="selector">A function that selects the union variant (e.g. based on the current JSON token type).</param>
535+
/// <param name="readType1">
536+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the first union variant type, or <see langword="null"/>
537+
/// to use the default converter for the type <typeparamref name="T1"/>.
538+
/// </param>
539+
/// <param name="readType2">
540+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the second union variant type, or <see langword="null"/>
541+
/// to use the default converter for the type <typeparamref name="T2"/>.
542+
/// </param>
543+
/// <param name="readType3">
544+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the third union variant type, or <see langword="null"/>
545+
/// to use the default converter for the type <typeparamref name="T3"/>.
546+
/// </param>
547+
/// <param name="readType4">
548+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the fourth union variant type, or <see langword="null"/>
549+
/// to use the default converter for the type <typeparamref name="T4"/>.
550+
/// </param>
551+
/// <returns>A boxed value of the selected union variant.</returns>
552+
/// <exception cref="InvalidOperationException">If no matching union variant could be selected.</exception>
553+
public static object? ReadUnionValue<T1, T2, T3, T4>(this ref Utf8JsonReader reader, JsonSerializerOptions options,
554+
JsonUnionSelectorFunc selector, JsonReadFunc<T1>? readType1, JsonReadFunc<T2>? readType2,
555+
JsonReadFunc<T3>? readType3, JsonReadFunc<T4>? readType4)
556+
{
557+
if (reader.TokenType is JsonTokenType.Null)
558+
{
559+
return null;
560+
}
561+
562+
return selector(ref reader, options) switch
563+
{
564+
1 => (readType1 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T1>(ref r, o))).Invoke(ref reader, options),
565+
2 => (readType2 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T2>(ref r, o))).Invoke(ref reader, options),
566+
3 => (readType3 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T3>(ref r, o))).Invoke(ref reader, options),
567+
4 => (readType4 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T4>(ref r, o))).Invoke(ref reader, options),
568+
_ => throw new InvalidOperationException($"Failed to select a union variant for union of type '{typeof(T1).Name}', '{typeof(T2).Name}', '{typeof(T3).Name}' or '{typeof(T4).Name}'.")
569+
};
570+
}
571+
572+
/// <summary>
573+
/// Reads an inline union value from a given <see cref="Utf8JsonReader"/> instance.
574+
/// </summary>
575+
/// <typeparam name="T1">The first union type.</typeparam>
576+
/// <typeparam name="T2">The second union type.</typeparam>
577+
/// <typeparam name="T3">The third union type.</typeparam>
578+
/// <typeparam name="T4">The fourth union type.</typeparam>
579+
/// <typeparam name="T5">The fifth union type.</typeparam>
580+
/// <param name="reader">A reference to the <see cref="Utf8JsonReader"/>.</param>
581+
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
582+
/// <param name="selector">A function that selects the union variant (e.g. based on the current JSON token type).</param>
583+
/// <param name="readType1">
584+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the first union variant type, or <see langword="null"/>
585+
/// to use the default converter for the type <typeparamref name="T1"/>.
586+
/// </param>
587+
/// <param name="readType2">
588+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the second union variant type, or <see langword="null"/>
589+
/// to use the default converter for the type <typeparamref name="T2"/>.
590+
/// </param>
591+
/// <param name="readType3">
592+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the third union variant type, or <see langword="null"/>
593+
/// to use the default converter for the type <typeparamref name="T3"/>.
594+
/// </param>
595+
/// <param name="readType4">
596+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the fourth union variant type, or <see langword="null"/>
597+
/// to use the default converter for the type <typeparamref name="T4"/>.
598+
/// </param>
599+
/// <param name="readType5">
600+
/// The <see cref="JsonReadFunc{T}"/> delegate that should be called to read the fifth union variant type, or <see langword="null"/>
601+
/// to use the default converter for the type <typeparamref name="T5"/>.
602+
/// </param>
603+
/// <returns>A boxed value of the selected union variant.</returns>
604+
/// <exception cref="InvalidOperationException">If no matching union variant could be selected.</exception>
605+
public static object? ReadUnionValue<T1, T2, T3, T4, T5>(this ref Utf8JsonReader reader, JsonSerializerOptions options,
606+
JsonUnionSelectorFunc selector, JsonReadFunc<T1>? readType1, JsonReadFunc<T2>? readType2,
607+
JsonReadFunc<T3>? readType3, JsonReadFunc<T4>? readType4, JsonReadFunc<T5>? readType5)
608+
{
609+
if (reader.TokenType is JsonTokenType.Null)
610+
{
611+
return null;
612+
}
613+
614+
return selector(ref reader, options) switch
615+
{
616+
1 => (readType1 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T1>(ref r, o))).Invoke(ref reader, options),
617+
2 => (readType2 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T2>(ref r, o))).Invoke(ref reader, options),
618+
3 => (readType3 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T3>(ref r, o))).Invoke(ref reader, options),
619+
4 => (readType4 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T4>(ref r, o))).Invoke(ref reader, options),
620+
5 => (readType5 ?? (static (ref Utf8JsonReader r, JsonSerializerOptions o) => ReadValue<T5>(ref r, o))).Invoke(ref reader, options),
621+
_ => throw new InvalidOperationException($"Failed to select a union variant for union of type '{typeof(T1).Name}', '{typeof(T2).Name}', '{typeof(T3).Name}', '{typeof(T4).Name}' or '{typeof(T5).Name}'.")
622+
};
623+
}
624+
484625
#region Specialized Read Methods
485626

486627
/// <summary>

0 commit comments

Comments
 (0)