How do I use RestSharp to deserialize class hierarchies? - c#

The HTTP response (text/xml):
<results>
<status code="ok"/>
<OWASP_CSRF_TOKEN>
<token>db8288c2e01a6e0caa5a9b52cc4570040b2714cc7a1f589670d6606d486ab98e</token>
</OWASP_CSRF_TOKEN>
</results>
My C# objects:
[DeserializeAs(Name = "results")]
public class Response
{
[DeserializeAs(Name = "status")]
public ResultStatus Status { get; set; }
public struct ResultStatus
{
[DeserializeAs(Name = "code")]
public string Code { get; set; }
}
}
internal class AuthenticationResponse : Response
{
[DeserializeAs(Name = "OWASP_CSRF_TOKEN")]
internal OWaspCsrfToken CsrfToken { get; set; }
internal struct OWaspCsrfToken
{
[DeserializeAs(Name = "token")]
internal string Token { get; set; }
}
}
The goal here is to have RestSharp deserialize the HTTP response to objects in code. However, for some reason only the result status's Code property is being properly set; the Token property is always null. What's the proper way to deserialize objects in class hierarchies like this?

The problem is with the use of internal. My guess is that properties, classes, and methods marked internal can't even be properly sniffed using reflection at runtime. Or, if they can be, RestSharp might have an issue doing this correctly. In any case, this works:
public class AuthenticationResponse : Response
{
[DeserializeAs(Name = "OWASP_CSRF_TOKEN")]
public OWaspCsrfToken CsrfToken { get; set; }
public struct OWaspCsrfToken
{
[DeserializeAs(Name = "token")]
public string Token { get; set; }
}
}
EDIT: Actually, I'm pretty sure reflection can see the internal methods. The problem was that RestSharp can't call the set method on any of the internal properties.

Related

how to put anonymous json as class member and convert all to json in c#

I'm having a well-defined class for sending as JSON body in an HTTP request.
public class EventData
{
public string deviceJobId { get; set; }
public int eventID { get; set; }
public long time_ms { get; set; }
/// similar fields
}
Now I have to add one more field called HealthInfo. The value of this new HealthInfo is a nested JSON read from some file.
The fields of this JSON file change from time to time, and there is no guarantee that some fields will always be present.
I don't want to read/modify any value of that and just need to publish this EventData as a json as part of an HTTP request.
Then how to add HealthInfo correctly?
I tried to put HealthInfo as string and object is getting double serialized.
you have to convert to JObject before you add new json string
JObject jo = JObject.FromObject(eventData);
jo["HealthInfo"] = jsonStringHealthInfo;
//or it could be (your question needs some details)
jo["HealthInfo"]=JObject.Parse(jsonStringHealthInfo);
StringContent content = new StringContent(jo.ToString(), Encoding.UTF8, "application/json");
var response = await client.PostAsync(api, content))
If you know all of the possible properties inside HealthInfo then you can create new class HealthInfo with nullable properties.
public class HealthInfo
{
public string? SomeData { get; set; }
public int? SomeOtherData { get; set; }
}
and then add nullable HealthInfo in your main class:
public class EventData
{
public string deviceJobId { get; set; }
public int eventID { get; set; }
public long time_ms { get; set; }
public HealthInfo? HealthInfo { get; set; }
/// similar fields
}
However if you're not sure what kind of data you're gonna get and want to avoid double serialization, just pass HealthInfo as object:
public class EventData
{
public string deviceJobId { get; set; }
public int eventID { get; set; }
public long time_ms { get; set; }
public object? HealthInfo { get; set; }
/// similar fields
}
You can use of C# reflection. (TypeBuilder.DefineProperty Method)
In fact you must add prop to the class in run time.
see full information at
https://learn.microsoft.com/

Creating Inherited Response Objects

I am writing some integration with a third-party eLearning platform that returns a variety of responses in different schemas depending on the function of my restful API call. Since these responses come back in several different schemas, I'm trying to create a series of response object classes that inherit a base Response object class that would contain the common JSON sections (aka "data" and "message") and allow each individual response object to override or have additional members/classes based on the response being returned.
Here are a couple examples of how the schemas may differ.
Class Creation Return:
{
"data": [
{
"row_index": 0,
"success": true,
"message": "string"
}
]
}
User Creation Return:
{
"data": [
{
"message": [
{
"id": "string",
"message": "string"
}
],
"success": true,
"user_id": 0
}
]
}
As you can see, the different responses have different schemas. The Class Create only returns a message member within the data object, and the User Create has a separate message object altogether.
Since I can't have a class called data within each object because of ambiguity, I'm thinking I need to create a Base Response Object that contains the common members and allows me to override or add on the fly as necessary.
I've tried to create a Base Class:
public class BaseResponse
{
public List<Data> data;
public class Data
{
public bool success { set; get; }
}
}
as well as an example derived class:
public class ClassroomResponse : BaseResponse
{
public class Data
{
public int row_index { get; set; }
public string message { get; set; }
}
}
I'm not sure if this is only possible with functions and not classes as I'm trying to do above? Is there a way to add additional members to the derived object's (row_index and message are not members of all responses, so I'd like to be able to grab those as needed)
You could either create individual, distinct classes for each type, which might be the right option here depending on the other variants you haven't shown. Or you can use generics. There's a few ways you can do this, but here is one way you might do it.
A base class for the overall response:
public abstract class Response<TData>
{
[JsonProperty("data")]
public TData Data { get; set; }
}
A base class for the Data objects:
public abstract class BaseData<TMessage>
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("message")]
public TMessage Message { get; set; }
}
The response type for class creation:
public class ClassData : BaseData<string>
{
[JsonProperty("row_index")]
public int RowIndex { get; set; }
}
The response types for user creation:
public class UserData : BaseData<UserMessage>
{
[JsonProperty("user_id")]
public int UserId { get; set; }
}
public class UserMessage
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
And finally the overall response types:
public class ClassResponse : Response<ClassData>
{ }
public class UserResponse : Response<UserData>
{ }
And now you can use the objects like you would normally:
var classData = new ClassResponse {Data = new ClassData {Message = ""}};
var classJson = JsonConvert.SerializeObject(classData);
var userData = new UserResponse {Data = new UserData {Message = new UserMessage {Message = ""}}};
var userJson = JsonConvert.SerializeObject(userData);
I will go by the generics route
Let's say that we have a response for Class creation as
public class ClassResponseObject {
public int row_index { get; set; }
public bool success { get; set; }
public string message { get; set; };
}
and for the User creation:
public class UserResponseObject {
public int user_id { get; set; }
public bool success { get; set; }
public MessageResponseObject message { get; set; };
}
and for the Message
public class MessageResponseObject {
public string id { get; set; }
public string message { get; set; };
}
after seeing the above code we are able to find that we have success property common in both the responses, so lets create a base class with that property and inherit these classes with that.
public class BaseResponseObject {
public bool success { get; set; }
}
public class ClassResponseObject : BaseResponseObject {
public int row_index { get; set; }
public string message { get; set; };
}
public class UserResponseObject : BaseResponseObject {
public int user_id { get; set; }
public MessageResponseObject message { get; set; };
}
at this point another common property we see is message, but both have different types. This can be solved with the use of generics. I am considering that there might be more types for the message property of the response, but it should work in either case.
for this let modify our BaseResponseObject and move the message property there
public class BaseResponseObject<TMessage> {
public bool success { get; set; }
public TMessage message { get; set; }
}
so our response objects will become something like this:
public class ClassResponseObject : BaseResponseObject<String> {
public int row_index { get; set; }
}
public class UserResponseObject : BaseResponseObject<MessageResponseObject> {
public int user_id { get; set; }
}
as the last step we need to define the final class for the actual response
public class APIResponse<TResponse> {
public List<TResponse> data { get; set; }
}
now when you are capture the response for the Class creation you can simply capture it in
APIResponse<ClassResponseObject>
similarly for the User creation, capture it in
APIResponse<UserResponseObject>
I hope this helps.

RestSharp: Execute<T>() with T having IEnumerable property

I noticed that using RestSharp :Execute<T>() , when T as below
public class Result
{
public List<DBData> Data { get; set; }
public int Total { get; set; }
public bool Success { get; set; }
}
It deserialized the JSON from Execute<Result>() correctly into Result object,
However when the class has IEnumerable property like below
public class Result
{
public IEnumerable<DBData> Data { get; set; }
public int Total { get; set; }
public bool Success { get; set; }
}
Execute<Result>() does not fill(deserialize) into the object Result.
I am suspecting it is because of IEnumerable<T> being read only and Restsharp is unable to deserialize data because of that ? Is it the case ?
Because RestSharp can not infer the type of the property from the IEnumerable. Extend the existing SimpleJson serializer to use more robust serializer like Json.Net

Newtonsoft.Json Custom Root Name for Deserialization

I have this class that defines the json format:
public class ResultType
{
public bool status { get; set; }
public string message { get; set; }
}
The actual json looks like this:
{"result":{"status":true,"message":"Success"}}
How can I override the root attribute when de-serializing the json to "result"
JObject jsonResponse = JObject.Parse(jsonString);
ResultType _Data = Newtonsoft.Json.JsonConvert.DeserializeObject<ResultType>(jsonResponse["result"].ToString());
Console.WriteLine(_Data.status);
Fiddle: https://dotnetfiddle.net/gjYS2p
I have a central deserialization method, so I'm trying to avoid type specific code as much as possible.
I used the following to resolve the problem, maybe not as sexy as I was hoping for but it works.
public class ResultType
{
public ResultDetailType result { get; set; }
}
public class ResultDetailType
{
public bool status { get; set; }
public string message { get; set; }
}

XML serialization where request element is wrapped up with multiple parent elements

I have a merchant account balance request class as follows:
[Serializable]
[XmlRoot(ElementName = "accountbalance", Namespace = "", IsNullable = false)]
public class MerchantAccountBalanceRequest
{
[XmlElementAttribute("agent")]
public string Agent { get; set; }
[XmlElementAttribute("agentPin")]
public string AgentPin { get; set; }
}
Which will result in the following XML:
<accountbalance>
<agent>aaaaaa</agent>
<agentPin>mmmmmm</agentPin>
</accountbalance>
Could I somehow put an attribute on my class so that for the MerchantAccountBalanceRequest object I will get the following XML:
<Envelope>
<Body>
<accountbalance>
<agent>aaaaaa</agent>
<agentPin>mmmmmm</agentPin>
</accountbalance>
</Body>
</Envelope>
That is, just wrap it up with Envelope and Body elements.
Whilst you can't achieve this with an attribute on your request class, you can wrap your request up in other classes representing the Envelope and Body elements:
public class MerchantAccountBalanceRequest
{
[XmlElement("agent")]
public string Agent { get; set; }
[XmlElement("agentPin")]
public string AgentPin { get; set; }
}
public class RequestBody
{
[XmlElement("accountbalance")]
public MerchantAccountBalanceRequest BalanceRequest { get; set; }
}
[XmlRoot(ElementName = "Envelope")]
public class RequestEnvelope
{
[XmlElement("Body")]
public RequestBody Body { get; set; }
}
When serializing and deserializing, construct your XmlSerializer object from RequestEnvelope instead of MerchantAccountBalanceRequest. You will have to set the RequestEnvelope.Body and RequestBody.BalanceRequest properties when constructing them (you could probably initialize the Body property in RequestEnvelope to a new RequestBody instance in the constructor, for convenience).
This looks like a single request type in some protocol, so if you have multiple request types, you could create a base-class for the requests (e.g. RequestBase), and adjust the RequestBody so that it accepts a choice of the possible request elements like in the following example:
public abstract class RequestBase
{
}
public class SomeOtherRequest : RequestBase
{
[XmlElementAttribute("example")]
public string Example { get; set; }
}
public class MerchantAccountBalanceRequest : RequestBase
{
[XmlElement("agent")]
public string Agent { get; set; }
[XmlElement("agentPin")]
public string AgentPin { get; set; }
}
public class RequestBody
{
[XmlElement(ElementName = "accountbalance", Type = typeof(MerchantAccountBalanceRequest))]
[XmlElement(ElementName = "somethingelse", Type = typeof(SomeOtherRequest))]
public RequestBase Request { get; set; }
}

Categories