Skip to content

Instantly share code, notes, and snippets.

@SteffenBlake
Last active December 12, 2025 21:40
Show Gist options
  • Select an option

  • Save SteffenBlake/67c4c7a8b517ff26d68e8694168cdbce to your computer and use it in GitHub Desktop.

Select an option

Save SteffenBlake/67c4c7a8b517ff26d68e8694168cdbce to your computer and use it in GitHub Desktop.
/// <summary>
/// Automatically detects dotVariant union types and properly re-schemas
/// them to instead be registered with oneOf OpenApi spec
/// </summary>
public class DotVariantSchemaTransformer : IOpenApiSchemaTransformer
{
public async Task TransformAsync(
OpenApiSchema schema,
OpenApiSchemaTransformerContext context,
CancellationToken cancellationToken
)
{
// Step one: confirm it is a public class
var type = context.JsonTypeInfo.Type;
if (!type.IsClass || !type.IsPublic)
{
return;
}
// Step 2: It should have a void Visit(...) function
var visit = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(m =>
m.Name == "Visit" &&
!m.IsGenericMethod &&
m.ReturnType == typeof(void)
);
if (visit == null)
{
return;
}
var unionTypes = new HashSet<Type>();
// Step 3: all params of the visit function should be Action<SomeType>
// Extract out all those SomeTypes
foreach (var p in visit.GetParameters())
{
if (!p.ParameterType.IsGenericType)
{
return;
}
var def = p.ParameterType.GetGenericTypeDefinition();
if (def != typeof(Action<>))
{
return;
}
var unionType = p.ParameterType.GetGenericArguments()[0];
unionTypes.Add(unionType);
}
// Step 4: clear out the non-exposed properties that show up defaul
// on a dotVariant type (IsEmpty, Index)
schema.Properties = null;
// Step 5: Convert all the extracted types and register them as OneOfs
schema.OneOf = [];
foreach(var unionType in unionTypes)
{
schema.OneOf.Add(
await context.GetOrCreateSchemaAsync(
unionType, cancellationToken: cancellationToken
)
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment