2.21JSON Response Deserialization Wrapper

Created on Aug 17, 2026~6 min read

When communicating with third-party APIs over HTTP remote calls, a unified JSON response structure is usually returned, such as the ApiResult<T> type, where the actual data is stored in the Data property:

cs
public class ApiResult<T>{    public bool Success { get; set; }    public T? Data { get; set; }    // Actual returned data}

When the JSON response deserialization wrapper feature is not enabled, each call needs to explicitly specify the ApiResult<T> type:

cs
var content = await httpRemoteService.SendAsAsync<ApiResult<string>>(    HttpRequestBuilder.Get("https://furion.net"));

Enabling

1. Enable Once

To simplify the calling flow, you can configure the JSON response deserialization wrapper so that it automatically extracts the contents of the Data property:

cs
// Configure the default HTTP clientservices.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    {        options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data));    });

Once configured, enable the feature by calling UseJsonResponseWrapper(). After that, you only need to specify the target data type without repeatedly declaring ApiResult<T>:

cs
var content = await httpRemoteService.SendAsAsync<string>(    HttpRequestBuilder.Get("https://furion.net").UseJsonResponseWrapper());

The framework will automatically create an ApiResult<string> instance at runtime and return the value of its Data property.

2. Enable Globally (Takes Effect for All Requests by Default)

You can also enable the JSON response deserialization wrapper feature globally by simply setting UseJsonResponseWrapper to true:

cs
// Configure the default HTTP clientservices.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    {        options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data));        options.UseJsonResponseWrapper = true;    });

After global enablement, all requests use the wrapper feature by default:

cs
var content = await httpRemoteService.SendAsAsync<string>(    HttpRequestBuilder.Get("https://furion.net")); // No need to explicitly call UseJsonResponseWrapper()

3. Disable Once (Override the Global Setting)

If you need to disable the feature for a specific request, call the following method:

cs
var content = await httpRemoteService.SendAsAsync<ApiResult<string>>(    HttpRequestBuilder.Get("https://furion.net").UseJsonResponseWrapper());

By default, not calling UseJsonResponseWrapper() means the feature is not enabled, in which case you must pass the complete response type, unless UseJsonResponseWrapper = true is configured globally.

Custom Result Handling (ResultHandler)

Sometimes, in addition to extracting Data, you also need to perform additional validation or transformation on the response. This can be achieved through the ResultHandler callback:

cs
// Configure the default HTTP clientservices.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    {        options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data))        {            ResultHandler = context =>            {                if (context.Instance is { } instance)                {                     // Can access the wrapper type instance to get any of its properties                    var success = context.GetPropertyValue<bool>(nameof(ApiResult<>.Success));                }                // For example, ensure the request succeeded                context.ResponseMessage.EnsureSuccessStatusCode();                // Return the final target result (i.e. the value of Data)                return context.Result;            }        };    });

Through ResultHandler, you can execute any custom logic (such as validation, transformation, or exception handling) before returning the final data, making request handling more flexible.

The type of the context parameter is JsonResponseWrapperContext, which contains the following properties and methods:

  • Properties:

    • Instance: The concrete instance of the wrapper type (such as ApiResult<T>, of type object?).
    • Result: The target result (i.e. the value of Data, of type object?).
    • ResponseMessage: The response message (of type HttpResponseMessage).
  • Methods:

    • GetPropertyValue<T>(propertyName): Gets the value of a specified property of the concrete wrapper type (i.e. Instance).