QueryStringStrategy and collections

I’m creating my own client to consume the API over at https://apidocs.chargebee.com/docs/api/subscriptions

I am using the .PostToUrl() set of methods.

I have configured to use my own JsConfig scope, and Im serializing my requests with that scope, plus using QueryStringSerializer.ComplexTypeStrategy = QueryStringStrategy.FormUrlEncoded;

Now things are great for a request like this:

    [Route("/customers", "POST")]
    public class CreateCustomerRequest : IReturn<CreateCustomerResponse>, IPost
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string Phone { get; set; }

        public string Email { get; set; }

        public ChargebeeAddress BillingAddress { get; set; }
    }

on the wire it is correctly converted to this:

first_name=afirstname
&last_name=alastname
&phone=%2b64277888111
&email=testing%5fonly%40company.com
&billing_address[first_name]=afirstname
&billing_address[last_name]=alastname
&billing_address[email]=testing%5fonly%40company.com
&billing_address[line1]=40+Taranaki+Street
&billing_address[line2]=Level+4
&billing_address[line3]=Room+2
&billing_address[city]=Wellington
&billing_address[state_code]=Wellington
&billing_address[zip]=6011
&billing_address[country]=NZ

Now this request, which includes a List<T> is not so great:

    [Route("/customers/{CustomerId}/subscription_for_items", "POST")]
    public class CreateSubscriptionRequest : IReturn<CreateSubscriptionResponse>, IPost
    {
        [ExcludeFromBody]
        public string CustomerId { get; set; }

        public List<SubscriptionItem> SubscriptionItems { get; set; }
    }

is converted on the wire to this:

subscription_items={item_price_id:testingonlydefault,quantity:1,unit_price:0}

Which is not the way ChargeBee likes it.

ChargeBee wants me to send that List<SubscriptionItem> like this:

subscription_items[item_price_id][0]="testingonlydefault"
&subscription_items[quantity][0]=1
&subscription_items[unit_price][0]=0

What do I need to extend to do this myself?

I did try to provide my own QueryStringSerializer.ComplexTypeStrategy but it does not get called for any field of the request DTO that is an IEnumerable! which defeats me.

There’s limited support for using ServiceStack’s typed Request DTOs to support 3rd Party APIs, but StripeGateway.cs makes use of the available customization options to customize Request DTOs to support their API conventions.

E.g. this uses IUrlFilter to override how the RequestedCapabilities is serialized by ignoring default serialization with [IgnoreDataMember] then using IUrlFilter.ToUrl() to customize the resulting URL is used:

[Route("/accounts")]
public class CreateStripeAccount : IPost, IReturn<CreateStripeAccountResponse>, IUrlFilter
{
    public string Country { get; set; }
    public string Email { get; set; }
    public StripeAccountType Type { get; set; }
    public StripeTosAcceptance TosAcceptance { get; set; }
    public StripeLegalEntity LegalEntity { get; set; }
    [IgnoreDataMember]
    public StripeCapability[] RequestedCapabilities { get; set; }

    public string ToUrl(string absoluteUrl)
    {
        if (RequestedCapabilities?.Length > 0)
        {
            foreach (var capability in RequestedCapabilities)
            {
                absoluteUrl = absoluteUrl.AddQueryParam($"capabilities[{capability}][requested]", true);
            }
        }
        return absoluteUrl;
    }
}

Beautiful! thank you.