Is there something like XmlRootAttribute that can be used with System.Text.JsonSerializer?
I need to be able to download data from this vendor using both XML an JSON. See sample data here:
{
    "categories": [
        {
            "id": 125,
            "name": "Trade Balance",
            "parent_id": 13
        }
    ]
}
Note the data elements are wrapped by an array named categories.  When downloading using XML I can set the root element to categories and the correct object is returned (see XMLClient below ).  When using JSONClient however I cannot (or do not know how) to set the root element.  Best workaround I could find is to use JsonDocument which creates a string allocation.  I could also create some wrapper classes for the JSON implementation but that is a lot of work that involves not only creating additional DTOs but also requires overwriting many methods on BaseClient.  I also don't want to write converters - it pretty much defeats the purpose of using well-known serialization protocols.  (Different responses will have a different root wrapper property name.  I know the name in runtime, but not necessarily at compile time.)
public class JSONClient : BaseClient
{
    protected override async Task<T> Parse<T>(string uri, string root)
    {
        uri = uri + (uri.Contains("?") ? "&" : "?") + "file_type=json";
        var document = JsonDocument.Parse((await Download(uri)), new JsonDocumentOptions { AllowTrailingCommas = true });
        string json = document.RootElement.GetProperty(root).GetRawText(); // string allocation 
        return JsonSerializer.Deserialize<T>(json);
    }
}
public class XMLClient : BaseClient
{
    protected override async Task<T> Parse<T>(string uri, string root)
    {
        return (T)new XmlSerializer(typeof(T), new XmlRootAttribute(root)).Deserialize(await Download(uri)); // want to do this - using JsonSerializer
    }
}
public abstract class BaseClient
{
    protected virtual async Task<Stream> Download(string uri)
    {
        uri = uri + (uri.Contains("?") ? "&" : "?") + "api_key=" + "xxxxx";
        var response = await new HttpClient() { BaseAddress = new Uri(uri) }.GetAsync(uri);
        return await response.Content.ReadAsStreamAsync();
    }
    protected abstract Task<T> Parse<T>(string uri, string root) where T : class, new();
    public async Task<Category> GetCategory(string categoryID)
    {
        string uri = "https://api.stlouisfed.org/fred/category?category_id=" + categoryID;
        return (await Parse<List<Category>>(uri, "categories"))?.FirstOrDefault();
    }
}
