File: Utilities\AIJsonUtilities.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.AI.Abstractions\Microsoft.Extensions.AI.Abstractions.csproj (Microsoft.Extensions.AI.Abstractions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using Microsoft.Shared.Diagnostics;
 
#pragma warning disable S1121 // Assignments should not be made from within sub-expressions
 
namespace Microsoft.Extensions.AI;
 
public static partial class AIJsonUtilities
{
    /// <summary>
    /// Adds a custom content type to the polymorphic configuration for <see cref="AIContent"/>.
    /// </summary>
    /// <typeparam name="TContent">The custom content type to configure.</typeparam>
    /// <param name="options">The options instance to configure.</param>
    /// <param name="typeDiscriminatorId">The type discriminator id for the content type.</param>
    /// <exception cref="ArgumentNullException"><paramref name="options"/> or <paramref name="typeDiscriminatorId"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentException"><typeparamref name="TContent"/> is a built-in content type.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="options"/> is a read-only instance.</exception>
    public static void AddAIContentType<TContent>(this JsonSerializerOptions options, string typeDiscriminatorId)
        where TContent : AIContent
    {
        _ = Throw.IfNull(options);
        _ = Throw.IfNull(typeDiscriminatorId);
 
        AddAIContentTypeCore(options, typeof(TContent), typeDiscriminatorId);
    }
 
    /// <summary>
    /// Adds a custom content type to the polymorphic configuration for <see cref="AIContent"/>.
    /// </summary>
    /// <param name="options">The options instance to configure.</param>
    /// <param name="contentType">The custom content type to configure.</param>
    /// <param name="typeDiscriminatorId">The type discriminator id for the content type.</param>
    /// <exception cref="ArgumentNullException"><paramref name="options"/>, <paramref name="contentType"/>, or <paramref name="typeDiscriminatorId"/> is <see langword="null"/>.</exception>
    /// <exception cref="ArgumentException"><paramref name="contentType"/> is a built-in content type or does not derived from <see cref="AIContent"/>.</exception>
    /// <exception cref="InvalidOperationException"><paramref name="options"/> is a read-only instance.</exception>
    public static void AddAIContentType(this JsonSerializerOptions options, Type contentType, string typeDiscriminatorId)
    {
        _ = Throw.IfNull(options);
        _ = Throw.IfNull(contentType);
        _ = Throw.IfNull(typeDiscriminatorId);
 
        if (!typeof(AIContent).IsAssignableFrom(contentType))
        {
            Throw.ArgumentException(nameof(contentType), "The content type must derive from AIContent.");
        }
 
        AddAIContentTypeCore(options, contentType, typeDiscriminatorId);
    }
 
    private static void AddAIContentTypeCore(JsonSerializerOptions options, Type contentType, string typeDiscriminatorId)
    {
        if (contentType.Assembly == typeof(AIContent).Assembly)
        {
            Throw.ArgumentException(nameof(contentType), "Cannot register built-in AI content types.");
        }
 
        IJsonTypeInfoResolver resolver = options.TypeInfoResolver ?? DefaultOptions.TypeInfoResolver!;
        options.TypeInfoResolver = resolver.WithAddedModifier(typeInfo =>
        {
            if (typeInfo.Type == typeof(AIContent))
            {
                (typeInfo.PolymorphismOptions ??= new()).DerivedTypes.Add(new(contentType, typeDiscriminatorId));
            }
        });
    }
}