skip to Main Content

I want to bind the configuration from a JSON file to an instance of HttpApiClientOptions. However, I encountered some issues when binding the Body property. After defining it as Dictionary<string, object>?, the Body property correctly contains the model and temperature key-value pairs, but the value of messages is empty, and I’m unable to retrieve {"role":"user", "content":"hello!"} from it.

C# Code:

var config = new ConfigurationBuilder()
    .AddJsonFile(configFilePath, false, false)
    .Build();
//...
config.GetSection("apiClientOptions").GetSection(apiClientName).Bind(apiClientOptions);

HttpApiClientOptions Class:

public class HttpApiClientOptions : BaseApiClientOptions
{
    public string Url { get; set; }

    public Dictionary<string, string>? Headers { get; set; }

    public Dictionary<string, string>? Params { get; set; }

    public Dictionary<string, object>? Body { get; set; }
}

JSON Configuration:

{
    "url": "https://example.com",
    "headers": {
        "Authorization": "Bearer sk-"
    },
    "params": {},
    "body": {"model": "gpt-3.5-turbo", "messages": [{"role":"user", "content":"hello!"}], "temperature": 0.7}
}

I attempted to define Body as JObject or JsonElement, but this approach also failed to retrieve values from the first level of nesting.

The reason I did not map the entire Body node to a specific type is that the contents and nesting depth under the Body node are uncertain. Is there any way to make the program read all the information inside the body node from the JSON file during configuration?

2

Answers


  1. Chosen as BEST ANSWER

    Now, I've also defined Body as Dictionary<string, string>, where the value contains the original content that was under the Body node. However, this approach becomes very cumbersome when filling out the JSON configuration file. Is there a better way to do this?

    Example of the "body" field in the JSON configuration file:

    "body": "{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "hello!"}], "temperature": 0.7}"
    

  2. You should understand one thing, configuration is not JSON, it is collection of key-value pairs, JSON files are not a single source of config. From the docs:

    Configuration in .NET is performed using one or more configuration providers. Configuration providers read configuration data from key-value pairs using various configuration sources:

    • Settings files, such as appsettings.json
    • Environment variables
    • Command-line arguments

    So Dictionary<string, object> does not make much sense to binder. One option – provide actual types for all levels:

    public class HttpApiClientOptions 
    {
        public string Url { get; set; }
        public Dictionary<string,string> Headers { get; set; }
        public Dictionary<string,string> Params { get; set; }
        public Body Body { get; set; }
    }
    
    public class Body
    {
        public string Model { get; set; }
        public List<Message> Messages { get; set; } // or List<Dictionary<string,string>>
        public double Temperature { get; set; }
    }
    
    public class Message
    {
        public string Role { get; set; }
        public string Content { get; set; }
    }
    

    Another way would be to exclude Body from the model and dynamically read config via GetChildren from some point and process results:

    public class HttpApiClientOptions 
    {
        public string Url { get; set; }
        public Dictionary<string,string> Headers { get; set; }
        public Dictionary<string,string> Params { get; set; }
    }
    

    And

    var body = builder.Configuration.GetSection("Body");
    foreach (var section in body.GetChildren())
    {
        // recursively process section
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search