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

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

Related

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.

How do I use RestSharp to deserialize class hierarchies?

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.

Deserialise JSON containing numeric key with Json.NET

I would like to deserialize the following JSON (using Json.NET) to an object, but cannot, as the class name would need to begin with a number.
An example of this is the Wikipedia article API. Using the API to provide a JSON response returns something like this. Note the "16689396" inside the "pages" key.
{
"batchcomplete":"",
"continue":{
"grncontinue":"0.893378504602|0.893378998188|35714269|0",
"continue":"grncontinue||"
},
"query":{
"pages":{
"16689396":{
"pageid":16689396,
"ns":0,
"title":"Jalan Juru",
"extract":"<p><b>Jalan Juru</b> (Penang state road <i>P176</i>) is a major road in Penang, Malaysia.</p>\n\n<h2><span id=\"List_of_junctions\">List of junctions</span></h2>\n<p></p>\n<p><br></p>"
}
}
}
}
How could I deserialize this JSON containing a number which changes based on the article?
It sounds like the Pages property in your Query class would just need to be a Dictionary<int, Page> or Dictionary<string, Page>.
Complete example with the JSON you've provided - I've had to guess at some of the name meanings:
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
public class Root
{
[JsonProperty("batchcomplete")]
public string BatchComplete { get; set; }
[JsonProperty("continue")]
public Continuation Continuation { get; set; }
[JsonProperty("query")]
public Query Query { get; set; }
}
public class Continuation
{
[JsonProperty("grncontinue")]
public string GrnContinue { get; set; }
[JsonProperty("continue")]
public string Continue { get; set; }
}
public class Query
{
[JsonProperty("pages")]
public Dictionary<int, Page> Pages { get; set; }
}
public class Page
{
[JsonProperty("pageid")]
public int Id { get; set; }
[JsonProperty("ns")]
public int Ns { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("extract")]
public string Extract { get; set; }
}
class Test
{
static void Main()
{
string text = File.ReadAllText("test.json");
var root = JsonConvert.DeserializeObject<Root>(text);
Console.WriteLine(root.Query.Pages[16689396].Title);
}
}
Related question: Json deserialize from wikipedia api with c#
Essentially you need to changes from using a class for the pages to a dictionary, which allows for the dynamic nature of the naming convention.
Class definitions :
public class pageval
{
public int pageid { get; set; }
public int ns { get; set; }
public string title { get; set; }
public string extract { get; set; }
}
public class Query
{
public Dictionary<string, pageval> pages { get; set; }
}
public class Limits
{
public int extracts { get; set; }
}
public class RootObject
{
public string batchcomplete { get; set; }
public Query query { get; set; }
public Limits limits { get; set; }
}
Deserialization :
var root = JsonConvert.DeserializeObject<RootObject>(__YOUR_JSON_HERE__);
var page = responseJson.query.pages["16689396"];
You can implement your own DeSerializer or editing the JSON before you DeSerialize it.

Serialize XML to a class with a different name

Lets say I've got an API response that looks like this
<ApiException>
<Status>400</Status>
<Message>Foot too big for mouth</Message>
<ApiException>
I know how to create a class called ApiException and serialize to that:
public class ApiException
{
public string Status { get; set; }
public string Message { get; set; }
}
using (var response = ((HttpWebResponse)wex.Response))
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
System.Xml.Serialization.XmlSerializer serializer =
new System.Xml.Serialization.XmlSerializer(typeof(ApiException));
ApiException ex = (ApiException)serializer.Deserialize(reader);
}
}
And I also know how to specify Element names for my properties
public class ApiException
{
[System.Xml.Serialization.XmlElement(ElementName = "Status")]
public string Whut { get; set; }
[System.Xml.Serialization.XmlElement(ElementName = "Message")]
public string Why { get; set; }
}
But what if I already have a class called ApiException? Say I want to call this one FootMouthAPIException
Is there any way to do that? Maybe an Data Annotation on the class itself?
You can use the XmlRoot attribute, e.g.
[XmlRoot(ElementName = "ApiException")]
public class FootMouthAPIException
{
}

Xml Deserialization - Sequence of Mutiple Types

Given the following fragment where links is a sequence of unbounded imagelinks and documentlinks, what should the deserailized class be?
<Values>
<Links>
<ImageLink>http://#</ImageLink>
<ImageLink>http://#</ImageLink>
<DocumentLink>http://#</DocumentLink>
</Links>
</Values>
Typically, if it was just an array of imagelinks I might have
public class Values
{
public imagelink[] ImageLinks { get; set; }
}
public class ImageLink
{
public string Value { get; set; }
}
But with the above xml I'm stumped.
Btw, I have no control over the xml.
This worked
public class DocumentLink : Link
{
}
public class ImageLink : Link
{
}
public class Link
{
[XmlText]
public string Href { get; set; }
}
public class Values
{
[XmlArrayItem(ElementName = "ImageLink", Type = typeof(ImageLink))]
[XmlArrayItem(ElementName = "DocumentLink", Type = typeof(DocumentLink))]
public Link[] Links { get; set; }
}
You should have a base class Link as follows
public class Link
{
public string Href { get; set; }
}
public class ImageLink : Link
{
}
public class DocumentLink : Link
{
}
And your values class would look like:
public class Values
{
public Link[] links { get; set; }
}
Alternatively, you could use ArrayList instead of strong typed array.

Categories