ServiceStack - Dynamic/Object in DTO - servicestack

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!

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; }
}

Related

Automapper - flattening of object property

let's say I have
public class EFObject
{
public int Id { get; set; }
public int NavId { get; set; }
public NavObject Nav { get; set; }
}
public class DTOObject
{
public int Id { get; set; }
public int NavId { get; set; }
public string NavName { get; set; }
}
My expectation was high, and I thought to my self the built-in flattening should handle this, so my mapping is very simple
CreateMap<DTOObject, EFObject>().ReverseMap();
Unfortunately, converting DTOObject to EFObject does not work as expected because EFObject.Nav is null. Since I used the name NavId and NavName I would expect it to create a new NavObject and set the Nav.Id and Nav.Name accordingly.
My Problem : Is there a feature in Automapper that will allow me to achieve the intended result without having to manually write a rule to create an NavObject when mapping the Nav property?.
Unflattening is only configured for ReverseMap. If you want unflattening, you must configure Entity -> Dto then call ReverseMap to create an unflattening type map configuration from the Dto -> Entity.
as noted by Automapper documentation here

JSON.NET Object Deserialisation with class

I am using these classes:
public class MasteryPages
{
internal MasteryPages() { }
[JsonProperty("pages")]
public List<MasteryPage> Pages { get; set; }
[JsonProperty("summonerId")]
public long SummonerId { get; set; }
}
[Serializable]
public class MasteryPage
{
internal MasteryPage() { }
[JsonProperty("current")]
public bool Current { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("talents")]
public List<Talent> Talents { get; set; }
}
[Serializable]
public class Talent
{
internal Talent() { }
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("rank")]
public int Rank { get; set; }
}
This is the code I'm using to deserialise the object
//MASTERIES
var jsonMasteries = requester.CreateRequest(string.Format(RootUrl, Region) + string.Format(MasteriesUrl, summonerId));
var objAllMasteryPages = JsonConvert.DeserializeObject<MasteryPages>(jsonMasteries);
The jsonMasteries object is correctly serialized and gives me this:
http://pastebin.com/3dkdDHdx (Rather large, to view easily: go to http://www.jsoneditoronline.org/ and paste it)
The second line is giving me troubles however. Normally my object should be filled with the data. It unfortunately isn't and I have no idea what's wrong.
Anyone could help me out?
Your problem is in this part of serialized JSON: "42177333": { ... }
As I understand - this is some kind of ID and it's dynamic.
Possible solutions are:
One of possible resolutions is here: C# deserialize dynamic JSON
Cut this part of dynamic JSON.
Try to modify the serialization stuff to avoid this dynamic ID.
Thanks to sleepwalker I saw what was wrong. (Dynamic Id (number), first line)
Now, the James Newtonking JSON library has a solution for dynamic id's like this.
I edited my code to this:
var jsonMasteries = requester.CreateRequest(string.Format(RootUrl, Region) + string.Format(MasteriesUrl, summonerId));
var objAllMasteriePages = JsonConvert.DeserializeObject<Dictionary<long, MasteryPages>>(jsonMasteries).Values.FirstOrDefault().Pages;
(First line stays the same, the magic is in the second line)
Now, i use a dictionary with the key being my given Id, and my custom class.
This works wonders

Generic-typed response object not accurately documented in Swagger (ServiceStack)

I'm having an issue with the ServiceStack implementation of Swagger with regards to the documentation of generic-typed response objects. Strongly-typed response objects are correctly documented and displayed, however once a generic-typed object is used as a response, the documentation is inaccurate and misleading.
Request DTO
[Route("/users/{UserId}", "GET", Summary = "Get a specific User Profile")]
public class GetUser : IReturn<ServiceResponse<UserProfile>>
{
[ApiMember(Description = "User Id", ParameterType = "path", IsRequired = true)]
public int UserId { get; set; }
}
Response DTO
public class ServiceResponse<T> : IServiceResponse<T>
{
public IList<string> Errors { get; set; }
public bool Successful { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public T Data { get; set; }
public ServiceResponse()
{
Errors = new List<string>();
}
}
Response DTO Type
public class UserProfile : RavenDocument
{
public UserProfile()
{
Races = new List<UserRace>();
Workouts = new List<Workout>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public DateTime? BirthDate { get; set; }
public Gender? Gender { get; set; }
public string UltracartPassword { get; set; }
public string UltracartCartId { get; set; }
[UniqueConstraint]
public string Email { get; set; }
public string ImageUrl { get; set; }
public FacebookUserInfo FacebookData { get; set; }
public GoogleUserInfo GoogleData { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? LastUpdated { get; set; }
public UserAddress ShippingAddress { get; set; }
public UserAddress BillingAddress { get; set; }
public IList<UserRace> Races { get; set; }
public IList<Workout> Workouts { get; set; }
}
The examples are pretty straight forward. Nothing really hacky or clever going on, however this is the sample documentation I get from Swagger out of the box:
As you can see, the generic type isn't documented correctly and some other type is used instead. As I am using this same ServiceResponse wrapper for all my responses, this is happening across the board.
As you have found, the ServiceStack swagger plugin does not currently attempt to handle generic types cleanly. A simple alternative that should work better is to make concrete subclasses of the generic types. e.g.:
public class UserProfileResponse : ServiceResponse<UserProfile> { ... }
public class GetUser : IReturn<UserProfileResponse> ...
This should be handled properly by Swagger.
I've found generic types aren't always a great fit for ServiceStack DTOs. You'll find many discussions (for example here, here and here) on StackOverflow that discuss this, and the reasons why concrete types and generally avoiding inheritance is a good idea for ServiceStack DTOs.
It takes effort to overcome the temptation to apply the DRY principle to request/respone DTOs. The way I think about it is that generics and inheritance are language features that facilitate implementation of algorithms in generic, reusable ways, where the generic method or base class doesn't need to know about the details of the concrete type. While DTOs may superficially have common structures that look like opportunities for inheritance or generics, in this case the implementation and semantics of each DTO is different for each concrete usage, so the details of each request/response message deserve to be defined explicitly.

REST Routing in ServiceStack

I just start to learn REST and ServiceStack and there's something about Route that I just can't quite understand. For example if we take the very basic HelloWorld example from GitHub tutorial and re-write it to return collection of User objects. Here is example:
public User
{
public string Name;
public string Address;
public int Age;
}
// Hello - request object without [Route] attribute
public class Hello
{
public string Name { get; set; }
}
public class HelloResponse
{
public IEnumerable<User> Result {get;set;}
}
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { // Collection of User object };
}
}
now everything working right and no problems here. But now I want to add another routing url like: /Hello/{name}/Address
Actually this call (GET) to this url will return a single User selected by Age parameter. How I can do this ? Should I add another Service ? And if the url will be:
/Hello/{name}/{age}/Address
It seems I don't understand something.....
See this earlier answer for details about Routing in ServiceStack. The Smart Routing section in ServiceStack's New API explains further options and different precedence.
There are a few problems with your example. First ServiceStack text serializers only support public properties so you need to change your User Model to use public properties instead of fields, e.g:
public User
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
}
Next, Interfaces on DTOs are a bad idea as there's no good reason for it. They're still supported but you can end up with undesirable results. Use a concrete collection like a List<T> which provides more utility, e.g:
public class HelloResponse
{
public List<User> Results { get; set; }
}
Also the routes should match the property names on your DTO exactly, they are case-insensitive when matching against the Request Path, but they need to map to an exact property name, e.g:
/Hello/{Name}/{Age}/Address

Serialize Object Azure Mobile Services

I am trying to serialize an object to Azure Mobile Services.
The object contains an array of a second object which should also be serialized.
[DataContract()]
class ObjectA
{
[DataMember(Name= "id")]
public int Id { get; set; }
[DataMember(Name = "info")]
public string info{ get; set; }
[DataMember(Name = "collectionOfB")]
public ObjectB[] myArrayOfB{ get; set; }
}
[DataContract()]
class ObjectB
{
[DataMember(Name= "id")]
public int Id { get; set; }
[DataMember(Name = "info")]
public string info{ get; set; }
}
I have loaded both table's properly and can insert an individual item into each of the tables.
However when I call the InsertAsync method on the table for objectA I receive an error
Cannot serialize member 'myArrayOfB' of type 'namespace.ObjectB[]' declared on type 'ObjectA'
Any idea's on what I need to do to fix this?
Mobile Services doesn't support serialization of arrays. There are two good posts here that show how you might support this:
http://blogs.msdn.com/b/carlosfigueira/archive/2012/08/30/supporting-arbitrary-types-in-azure-mobile-services-managed-client-simple-types.aspx
http://blogs.msdn.com/b/carlosfigueira/archive/2012/09/11/supporting-complex-types-in-azure-mobile-services-clients-implementing-1-n-table-relationships.aspx

Resources