Looking at the source code for the Stripe.Mapper<T>.MapFromJson
// the ResponseJson on a list method is the entire list (as json) returned from stripe.
// the ObjectJson is so we can store only the json for a single object in the list on that entity for
// logging and/or debugging
public static T MapFromJson(string json, string parentToken = null, StripeResponse stripeResponse = null)
{
var jsonToParse = string.IsNullOrEmpty(parentToken) ? json : JObject.Parse(json).SelectToken(parentToken).ToString();
var result = JsonConvert.DeserializeObject<T>(jsonToParse);
// if necessary, we might need to apply the stripe response to nested properties for StripeList<T>
ApplyStripeResponse(json, stripeResponse, result);
return result;
}
public static T MapFromJson(StripeResponse stripeResponse, string parentToken = null)
{
return MapFromJson(stripeResponse.ResponseJson, parentToken, stripeResponse);
}
private static void ApplyStripeResponse(string json, StripeResponse stripeResponse, object obj)
{
if (stripeResponse == null)
{
return;
}
foreach (var property in obj.GetType().GetRuntimeProperties())
{
if (property.Name == nameof(StripeResponse))
{
property.SetValue(obj, stripeResponse);
}
}
stripeResponse.ObjectJson = json;
}
which is deserializing the JSON using JSON.Net,
the fact that the StripeCharge.Invoice
property is marked with [JsonIgnore]
attribute.
#region Expandable Invoice
/// <summary>
/// ID of the invoice this charge is for if one exists.
/// </summary>
public string InvoiceId { get; set; }
[JsonIgnore]
public StripeInvoice Invoice { get; set; }
[JsonProperty("invoice")]
internal object InternalInvoice
{
set
{
StringOrObject<StripeInvoice>.Map(value, s => this.InvoiceId = s, o => this.Invoice = o);
}
}
#endregion
and finally how the InternalInvoice
property is mapped via the StringOrObject<T>
StringOrObject<StripeInvoice>.Map(value, s => this.InvoiceId = s, o => this.Invoice = o);
You can see in the class definition
internal static class StringOrObject<T>
where T : StripeEntityWithId
{
public static void Map(object value, Action<string> updateId, Action<T> updateObject)
{
if (value is JObject)
{
T item = ((JToken)value).ToObject<T>();
updateId(item.Id);
updateObject(item);
}
else if (value is string)
{
updateId((string)value);
updateObject(null);
}
}
}
That if the value passed is a string
, that it will set the Invoice
object property to null
else if (value is string)
{
updateId((string)value);
updateObject(null);
}
So the behavior you describe is as designed based on the shown data and code.
You would probably need to extract the InvoiceId
and try to retrieve it (the invoice) in order to use its members.