2

I am running into an issue while looking at SS.

I am writing a custom Stripe implementation and got stuck on web hooks, this in particular: https://stripe.com/docs/api#event_object

data->object - this can be anything.

Here is my DTO for it:

public class StripeEvent
{
    public string id { get; set; }

    public StripeEventData data { get; set; }

    public string type { get; set; }
}

[DataContract]
public class StripeEventData
{
    [DataMember(Name = "object")]
    public object _object { get; set; }
}

My hope is to basically just get that object as a string, and then parse it:

var invoice = (StripeInvoice)JsonSerializer.DeserializeFromString<StripeInvoice>(request.data._object.ToString());

Unfortunately the data that is returned from ToString does not have quotes surrounding each json property's name:

Capture

So, the DeserializeFromString returns an object that has everything nulled out.

Why does SS internally strip the quotes out? Is this the proper way to handle a json member that can be one of many different types? I did try the dynamic stuff, but did not have any luck with that either - basically the same result with missing quotes.

I searched very thoroughly for the use of objects and dynamic within DTOs, but there really was nothing that helped with this question.

Thank you!

Danny Huyn
  • 21
  • 2
  • 1
    I can't reproduce your problem. `request.data._object.ToString()` is a proper JSON object for me. Aside from that, this isn't a good approach; you should check the actual type of `object` and deserialize it into a first-class model. – Collin Dauphinee Dec 07 '16 at 21:36
  • Any suggestions on how to do that? The "type" value in StripeEvent indicates this, and I was going to make a switch statement to parse the json string into the respective class. – Danny Huyn Dec 07 '16 at 21:41
  • 1
    Sure; whatever is handling the `invoice.updated` event type should know that the `object` property is an `Invoice` – Collin Dauphinee Dec 07 '16 at 21:55
  • The problem is that I can't get it to serialize into the Invoice class. Since the quotes are gone, it's impossible. Is using "object" appropriate here? There is dynamic, but that didn't help. I seem to be missing something.. – Danny Huyn Dec 07 '16 at 21:58
  • 1
    No, something that receives the event should see that the event type is `invoice.updated`, then know to deserialize `data.object` as an `Invoice`. You shouldn't be deserializing `object.data` before you know what type it is; so basically, you want to deserialize in two steps: first the top-level event object to know what type the event is (ignoring it's nested data), then the nested data to get the correctly typed content. – Collin Dauphinee Dec 07 '16 at 22:00
  • Sorry, I should have clarified. I have that done already. StripeEvent gets deserialized just fine, I have a switch based on type, it branches to the part that handles invoice.created, but then it cannot deserialize data->object into Invoice. – Danny Huyn Dec 07 '16 at 22:07

1 Answers1

2

The issue is that you should never have an object type in DTOs as the serializer has no idea what concrete type to deserialize back into.

The Stripe documentation says object is a hash which you should be able to use a Dictionary to capture, e.g:

public class StripeEventData
{
    public Dictionary<string,string> @object { get; set; }
}

Or as an alternative you could use JsonObject which provides a flexible API to access dynamic data.

This will work for flat object structures, but for complex nested object structures you'll need to create Custom Typed DTOs, e.g:

public class StripeEventInvoice
{
    public string id { get; set; }    
    public StripeEventDataInvoice data { get; set; }
    public string type { get; set; }
}

public class StripeEventData
{
    public StripeInvoice @object { get; set; }
}
Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Thanks! What is the best way to convert a dictionary or JsonObject into StripeInvoice? – Danny Huyn Dec 08 '16 at 00:05
  • The JsonObject is an alternative for the Dictionary not the whole StripeInvoice – mythz Dec 08 '16 at 00:06
  • I understand that, but it would be preferable to populate the class vs a dictionary/JsonObject. Is there a good way to convert either of those into my dto class (StripeInvoice)? – Danny Huyn Dec 08 '16 at 00:17
  • @DannyHuyn if the payload matches the StripeInvoice schema you can use `jsonObject.ConvertTo()` – mythz Dec 08 '16 at 00:22
  • Thanks - almost done! That does all the top-level stuff, like "id" and "closed", but the deeper stuff, like the "lines" in StripeInvoice, do not get populated. If I make my StripeEventData's "object" property a StripeInvoice instead of JsonObject, it gets filled in, so it appears that ConvertTo doesn't work completely in this case. Am I missing something? Pastebin for reference: http://pastebin.com/raw/AUbaeGJg – Danny Huyn Dec 08 '16 at 00:48
  • No it only supports converting a flat object structure not a nested one. – mythz Dec 08 '16 at 00:51
  • Oh I see, that's a problem in this case - any workarounds? – Danny Huyn Dec 08 '16 at 00:54
  • Any further ideas? Still haven't been able to find a good solution for this. – Danny Huyn Dec 08 '16 at 16:33
  • @DannyHuyn Other than flatting the structure you're sending, i.e. so it can be sent with a Dictionary, for nested complex object graphs you'd need to use Custom DTOs as seen in my modified answer. – mythz Dec 08 '16 at 18:47
  • I have something very similar to that, but I also need it to work with other event types besides invoice, so forcing it as StripeInvoice is not ideal. I may have found a way though.. request.data.@object.ToJson() - that returns valid json, which I can then use DeserializeFromString on, but this still seems kinda messy to me.. – Danny Huyn Dec 08 '16 at 19:00
  • Is that the way you would implement your Stripe webhooks? Surprisingly, I couldn't find anything in your Stripe lib. – Danny Huyn Dec 08 '16 at 20:14
  • @DannyHuyn I'd never use an object to send unknown nested data structures like this which is going to be a source of runtime/interoperability issues. I'd either use a Dictionary or serialize and send the payload as a string (e.g using the more compact JSV format). – mythz Dec 08 '16 at 20:21
  • I agree the way Stripe does this is not ideal. Anyway, this appears to work, and will work for all different Stripe types besides invoices: http://pastebin.com/raw/Wxs0uVH5 I will probably use this, unless you have another suggestion? Looking at what ToJson does, it seems rather redundant to serialize to string and then deserialize from string in my code, so it seems like there may be a slightly better way hiding somewhere in your lib. – Danny Huyn Dec 08 '16 at 21:08
  • Any suggestion for an alternative or is that way I outlined the best/only way to get this done? Just seems like there may be a cleaner way - let me know your thoughts. – Danny Huyn Dec 09 '16 at 16:06
  • Sorry to bug, but I am still eagerly awaiting your advice (-: – Danny Huyn Dec 12 '16 at 17:02
  • So just to clarify, this is the best way? http://pastebin.com/raw/iynMNYiV It just seems redundant to convert it to a string and then back to json. It seems like there should be a cleaner way to handle a DTO property that can be one of several different structures. Let me know your thoughts. This works, but it looks inefficient to me. – Danny Huyn Jan 11 '17 at 22:51
  • You out there? Surprised to not have heard from you yet. – Danny Huyn Feb 04 '17 at 17:16