Search code examples
c#deserializationrefit

deserialization to class not working correctly


Trying to capture the Digicert Automation API with a Refit HttpClient Factory.

When my interface method is declared to return a string Task<string> GetAgentList([Body] GetAgentListReq req); I can see from the string returned that I'm getting the expected JSON result from the API. In particular the data member is returning an agentList member with a JSON array of agents.

When I change the interface method to return the defined result class Task<AgentList> GetAgentList([Body] GetAgentListReq req); The deserialization happens without throwing an exception, but the data.agentList member is null.

Since direct deserialization of the string using System.Text.Json.JsonSerializer.Deserialize<AgentList> also returns an object with a null data.agentList member, I'm going to operate under the assumption that my class is somehow wrong. If I solve this I'll post an Answer.

Here's my interface and classes:

using Refit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Digicert.Automation.API.Client
{
    [Headers("X-DC-DEVKEY: redacted")]
    public interface IDigicertAutomation
    {


        [Post("/agent/basicdetails")]
        Task<ApiResponse<IEnumerable<AgentSummary>>> GetAgentSummary(AgentSummaryReq req);

        [Post("/agent/list")]
        //Task<string> GetAgentList([Body] GetAgentListReq req);
        Task<AgentList> GetAgentList([Body] GetAgentListReq req);
    }



    public class AgentSummaryReq
    {
        public required string licensekey { get; set; }
        public int accountId { get; set; }
        public int divisionId { get; set; }
    }


    public class GetAgentListReq
    {
        public int accountId { get; set; }
        public class Criterion
        {
            public required string key;
            public required string operation;
            public required List<string> value;
        }
        public required Criterion[] searchCriteriaList { get; set; }
        public required int[] divisionIds { get; set; }
        public int startIndex;
        public int pageSize;
        public required Dictionary<string, string> sorting { get; set; }
    }

    public class Error
    {
        public required string message { get; set; }
        public required List<string> detailedMessage { get; set; }
        public required string errorCode { get; set; }
    }

    public class AgentSummary
    {
        public required Error error { get; set; }
        public class Data
        {
            public required string licenseKey { get; set; }
            public required string agentName { get; set; }
            public int updatePreference { get; set; }
            public required string stateString { get; set; }
            public required string agentVersion { get; set; }
            public bool updateAvailable { get; set; }
            public bool publishUpgradeOption { get; set; }
            public required string emailAddresses { get; set; }
        }
        public required Data data { get; set; }
    }
    public class AgentList
    {
        public required string error { get; set; }
        public class AgentListdata
        {
            public required List<AgentInfo> agentList;
        } 
        public required AgentListdata data {get;set;}
    }

    public class AgentInfo
    {

        public int agentId { get; set; }
        public required string accountId { get; set; }
        public required string divisionId { get; set; }
        public required string hostname { get; set; }
        public required string agentName { get; set; }
        public required string status { get; set; }
        public required string sensorName { get; set; }
        public required string type { get; set; }
        public required string agentLicense { get; set; }
        public int updatePreference { get; set; }
        public bool updateAvailable { get; set; }

    }
}

And my console app calling the API:

using Microsoft.Extensions.Hosting;
using Refit;
using Digicert.Automation.API;
using Microsoft.Extensions.DependencyInjection;
using Digicert.Automation.API.Client;
using static Digicert.Automation.API.Client.GetAgentListReq;
using System.Diagnostics;

namespace ConsoleApp1
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            using IHost host = Host.CreateDefaultBuilder(args)
                .ConfigureServices((_, services) =>
                {
                    services
                    .AddRefitClient<IDigicertAutomation>()
                    .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://caas.digicert.com/automationws/v1/"));
                }).Build();

            var automationClient = host.Services.GetRequiredService<IDigicertAutomation>();

            var sorting = new Dictionary<string, string>
            {
                { "AGENTNAME", "ASC" },
            };
            var req = new GetAgentListReq() { accountId = 1349264, searchCriteriaList = Array.Empty<Criterion>(), divisionIds = Array.Empty<int>(), startIndex = 0, pageSize = 50, sorting = sorting };


            var results = await automationClient.GetAgentList(req);
            //var result = results.Content;
            //var x = results.RequestMessage?.Content;
            
            //if (result?.data != null && result?.data.agentList != null)
            //{
            //    foreach (var a in result.data.agentList)
            //    {
            //        Console.Out.WriteLine(a.agentName);
            //    }
            //}
        }
    }
}

Solution

  • This was the ultimate set of classes to deserialize the returned JSON. Some are marked required and others marked nullable. The required were added first to suppress warnings, then apparently some fields were missing in the returned JSON so I had to back those properties out to nullable.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Digicert.Automation.API.Client
    {
        using System.Collections.Generic;
        using System.Text.Json.Serialization;
    
        public class AgentList
        {
            public required object Error { get; set; }
            public required Data Data { get; set; }
        }
    
        public class Data
        {
            public double totalCount { get; set; }
            public double totalPages { get; set; }
            public required List<Agent> AgentList { get; set; }
        }
    
        public class Agent
        {
            public double AgentId { get; set; }
            public required string AccountId { get; set; }
            public required string DivisionId { get; set; }
            public required string Hostname { get; set; }
            public required string AgentName { get; set; }
            public required string Status { get; set; }
            public string? SensorLicenseKey { get; set; }
            public required string SensorName { get; set; }
    
            public string? HostIp { get; set; }
            public required string Type { get; set; }
            public string? AgentLicense { get; set; }
            public double UpdatePreference { get; set; }
            public bool UpdateAvailable { get; set; }
            public required string Tag { get; set; }
            public string? OsName { get; set; }
            // Add additional fields as necessary
        }
    
    
    }