REST api with C#.NET complex json data in body posting - c#

I want to use sms gateway in my app. that's why I've contact with an operator and the operator give me a api format.
URL: https://ideabiz.lk/apicall/smsmessaging/v2/outbound/3313/requests
Request header
Content-Type: application/json
Authorization: Bearer [access token]
Accept: application/json
Body
{
"outboundSMSMessageRequest": {
"address": [
"tel:+94771234567"
],
"senderAddress": "tel:12345678",
"outboundSMSTextMessage": {
"message": "Test Message"
},
"clientCorrelator": "123456",
"receiptRequest": {
"notifyURL": "http://128.199.174.220:1080/sms/report",
"callbackData": "some-data-useful-to-the-requester"
},
"senderName": "ACME Inc."
}
}
Now, I've code it :
RestClient client = new RestClient(#"https://ideabiz.lk/");
RestRequest req = new RestRequest(#"apicall/smsmessaging/v2/outbound/3313/requests", Method.POST);
req.AddHeader("Content-Type", #"application/json");
req.AddHeader("Authorization", #"Bearer " + accessToken.ToString());
req.AddHeader("Accept", #"application/json");
string jSon_Data = #"{'outboundSMSMessageRequest': {'address': ['tel:+94768769027'],'senderAddress': 'tel:3313','outboundSMSTextMessage': {'message': 'Test Message : " + System.DateTime.Now.ToString() + "'},'clientCorrelator': '123456','receiptRequest': {'notifyURL': 'http://128.199.174.220:1080/sms/report','callbackData': 'some-data-useful-to-the-requester'},'senderName': ''}}";
JObject json = JObject.Parse(jSon_Data);
req.AddBody(json);
IRestResponse response = client.Execute(req);
string x = response.Content.ToString();
Console.WriteLine(x.ToString());
When i execute this program, in the line
req.AddBody(json);
my system crash and give error message that:
An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll
How can i post complex JSON by using C#.NET ?

You have two problems here:
You need to set RequestFormat = DataFormat.Json before the call to AddBody:
req.RequestFormat = DataFormat.Json;
req.AddBody(json);
Without setting the parameter, RestSharp tries to serialize the JObject to XML and falls into an infinite recursion somewhere -- most likely trying to serialize JToken.Parent.
The more recent versions of RestSharp no longer use Json.NET as their JSON serializer:
There is one breaking change: the default Json*Serializer* is no longer
compatible with Json.NET. To use Json.NET for serialization, copy the code
from https://github.com/restsharp/RestSharp/blob/86b31f9adf049d7fb821de8279154f41a17b36f7/RestSharp/Serializers/JsonSerializer.cs
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new YourCustomSerializer();
RestSharp's new built-in JSON serializer doesn't understand JObject so you need to follow the instructions above if you are using one of these more recent versions, Create:
public class JsonDotNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonDotNetSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonDotNetSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
And then do:
RestRequest req = new RestRequest(#"apicall/smsmessaging/v2/outbound/3313/requests", Method.POST);
req.JsonSerializer = new JsonDotNetSerializer();

Related

RestSharp ignores JSON attributes when serializing instance [duplicate]

I have an object
var testTcc = new TrendingConfigurationConfigDto
{
TrendingConfigurationId =1,
ConfigId = 1,
DeviceId = 1,
Selected = true,
YAxisPosition = YAxisPosition.Left,
Order = 1,
Color = "#ffffff",
Configuration = new BO.Shared.Dtos.List.ConfigurationListDto
{
Id = 1,
Name = "configuration",
Alias = "configuationAlias",
EnableEdit = true,
IsBusinessItem = true
},
Device = new BO.Shared.Dtos.List.DeviceListDto
{
Id = 1,
Name = "Device"
}
};
when I serialized it into json as
var jsonTcc = SimpleJson.SerializeObject(testTcc);
it returned string containing json object with YAxisPosition = 1, and when I tried deserializing it using
testTcc = SimpleJson.DeserializeObject<TrendingConfigurationConfigDto>(jsonTcc);
It is giving an exception System.InvalidCastException with message 'Specified cast is not valid'.
I tried changing YAxisPosition value in json string to string "1" or "Left" it was always giving me the same error, until I removed the property YAxisPosition from json string.
I might be missing some thing (an Attribute on enum property or something similar).
Please help me finding a way so that I can Serialize and De-serialize an object which contains Enum type property, using RestSharp.
Note: I tried successful serialization and de-serialization using NewtonSoft. but I do not want a dependency of my Web API Client on NetwonSoft, as I am already using RestSharp.
RestSharp removed JSON.NET support in v103.0. The default Json Serializer is no longer compatible with Json.NET. You have a couple of options if you want to continue using JSON.NET and maintain backwards compatibility. Beyond that, JSON.NET has more capability and may solve your issues over using the basic .NET serializer which RestSharp now depends on.
Plus you can use the [EnumMember] properties to define custom mappings during deserialization.
Option 1: Implement a custom serializer that uses JSON.NET
To use Json.NET for serialization, copy this code:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
public class JsonNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new JsonNetSerializer();
Option 2: Use a nuget package to use JSON.NET
Instead of doing all of that and having a custom JSON serializer spread throughout your projects is to just use this nuget package: https://www.nuget.org/packages/RestSharp.Newtonsoft.Json. It allows you to use an inherited RestRequest object that defaults to using Newtonsoft.JSON internally like this:
var request = new RestSharp.Newtonsoft.Json.RestRequest(); // Uses JSON.NET
The other option is to set it on every request like this:
var request = new RestRequest();
request.JsonSerializer = new NewtonsoftJsonSerializer();
Disclaimer: I created this project after getting frustrated with having a custom serializer laying around in my projects. I created this to keep things clean and hopefully help others who want backwards compatibility with their RestSharp code that worked prior to v103.
Found solution for this problem:
private IRestClient GetRestClient()
{
return new RestClient(url)
.AddDefaultHeader("Authorization", $"Bearer {token.AccessToken}")
.AddDefaultHeader("Accept", "*/*")
.AddDefaultHeader("Accept-Encoding", "gzip, deflate, br")
.AddDefaultHeader("Connection", "close")
.UseSystemTextJson(new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
});
}
I.e. instructed RestSharp to use System.Text.Json serializer and then instructed the serializer to use JsonStringEnumConverter class to serialize/deserialize enums.
I got it working with the help of PocoJsonSerializerStrategy. RestSharp allows you to specify your own serialization/deserialization strategy, so I created my own strategy that handles enums for me:
public class HandleEnumsJsonSerializerStrategy : PocoJsonSerializerStrategy
{
public override object DeserializeObject(object value, Type type)
{
if (type.IsEnum)
return Enum.Parse(type, (string)value);
else
return base.DeserializeObject(value, type);
}
}
Now you can pass an object of this class to SimpleJson.DeserializeObject call, like this and your enums are handled elegantly:
SimpleJson.DeserializeObject<JsonObject>(Response.Content, Strategy);
If you are using Enumeration class pattern you can user a JsonConverter approach. It means you need to create a custom converter and use JsonConverter attribute on the property you need to serialize. Example is following:
using Newtonsoft.Json;
using System;
namespace MyAwesomeAPI
{
public class EnumerationConverter<TEnum> : JsonConverter
where TEnum : Enumeration
{
public override bool CanConvert(Type objectType) => objectType == typeof(TEnum);
public override bool CanRead { get => true; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var uknown = Enumeration.FromDisplayName<TEnum>("Unknown");
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
var enumText = reader.Value?.ToString();
if (string.IsNullOrEmpty(enumText))
{
return uknown;
}
try
{
return Enumeration.FromDisplayName<TEnum>(enumText);
}
catch (Exception)
{
return uknown;
}
}
return uknown;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
}
Where our enum looks something like:
public class CardType : Enumeration
{
public static CardType Unknown = new(0, nameof(Unknown));
public static CardType Amex = new(1, nameof(Amex));
public static CardType Visa = new(2, nameof(Visa));
public static CardType MasterCard = new(3, nameof(MasterCard));
public CardType(int id, string name) : base(id, name)
{
}
}
And last thing is apply the attribute to a property
public class CreditCardResponse
{
[JsonProperty("card_number")]
public string CardNumber { get; set; }
[JsonProperty("card_type")]
[JsonConverter(typeof(EnumerationConverter<CardType>))]
public CardType CardType { get; set; }
}

JsonSerializer.DeserializeAsync<T> Not deserializing

I am working with my first Blazor app and running into a strange problem. I have a Web API that returns a fairly basic JSON response:
{
"entityId": 26,
"notifications": [
{
"type": "Success",
"message": "The operation completed successfully."
}
],
"hasErrors": false,
"hasWarnings": false
}
There is a standard POCO class that matches the above properties. From the Blazor app, when I try to get the response from an Http.PutAsJsonAsync<T>, an instance of the POCO class is created (so it's not null and doesn't throw an error), but none of the values above are actually present. The EntityId property is null and Notifications is instantiated but empty. The way I'm trying to access the response is:
var result = await Http.PutAsJsonAsync<ManufacturerModel>($"manufacturers", (ManufacturerModel)context.Model);
if (result.IsSuccessStatusCode)
{
var response = await JsonSerializer.DeserializeAsync<EntityResponseModel>(result.Content.ReadAsStream());
//response isn't null - it's just a newly created object with nothing mapped
}
Via the console in Chrome, I've confirmed the proper JSON is returned so it's really confusing why it'd create a new instance of that class, but not map any of the values.
Any ideas?
**** EDIT - including POCO definition ****
public class EntityResponseModel : BaseModel
{
/// <summary>
/// Gets or sets the Id of the affected entity
/// </summary>
public long? EntityId { get; set; }
}
public class BaseModel
{
public BaseModel()
{
this.Notifications = new EntityNotificationCollection();
}
#region Validation
/// <summary>
/// Adds a notification to this model.
/// </summary>
/// <param name="type">The type of notification</param>
/// <param name="message">The message to display</param>
public void AddNotification(EntityNotificationTypes type, string message)
{
this.Notifications.Add(new EntityNotification { Type = type, Message = message });
}
/// <summary>
/// Gets or sets the collection of notifications
/// </summary>
public EntityNotificationCollection Notifications { get; private set; }
/// <summary>
/// Gets whether errors exist on this model.
/// </summary>
//[JsonIgnore]
public bool HasErrors { get => this.Notifications.HasErrors; }
/// <summary>
/// Gets whether warnings exist on this model
/// </summary>
//[JsonIgnore]
public bool HasWarnings { get => this.Notifications.HasWarnings; }
#endregion
}
You can specify your serialization settings and define it as being case sensitive or insensitive.
CharlieFace provided this answer above.
Looks like you need to add your JsonAttribute to managing your case sensitivity.
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
This is my full solution:
var retList = new List<PocoSomeClassData> ();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
using (var reponseStream = await response.Content.ReadAsStreamAsync())
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
retList = await JsonSerializer.DeserializeAsync < List<PocoSomeClassData> > (reponseStream,options);
}
}

FCM notification from web api c# to android

I'm developing API via c# that will send notification to specific user ( android user) , then when user open the notification I want to redirect him to specific activity.
So I needed to send data along with notification message. I've tested it using Firebase Console and it's working fine , The notification is received and my launcher activity receive the extra from data has been sent
I've also tested it from my backend and the notification is received except that my launcher intent doesn't receive any extra.
I've been struggling for hours now , Any idea would help !
this is my code from c#
public String getNotification ()
{
string serverKey = "xxxx";
var result = "-1";
try
{
var webAddr = "https://fcm.googleapis.com/fcm/send";
var regID = "xxxx";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("Authorization:key=" + serverKey);
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"to\": \"" + regID +
"\",\"notification\": {\"title\": \"Testing\",\"body\": \"Hi Testing\"}" +
"," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return "Can't Send";
}
}
}
And this is my launcher activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("test" , "in main");
if (getIntent().getStringExtra("mymsg") != null) {
Log.d("test" , "has extra");
Intent intent = new Intent(this, Main2Activity.class);
startActivity(intent);
finish();
} else {
Log.d("test" , "no extra");
}
It looks like you have the wrong JSON:
," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }} will be:
"data:" {
"mymsg":"h"
}
just correct your JSON. But I recommend using c# classes and serialization. Look at this simple example:
var payload = new {
to = "XXXX",
notification = new
{
body = "Test",
title = "Test"
},
data = new {
mymsg = "h"
}
}
// Using Newtonsoft.Json
string postbody = JsonConvert.SerializeObject(payload).ToString();
But its just example. You should create classes instead of anonym objects and using JsonProperty or another way to serialize the object. Something like that:
/// <summary>
/// Data for sending push-messages.
/// </summary>
public class PushData
{
/// <summary>
/// [IOS] Displaying message
/// </summary>
[JsonProperty("alert")]
public Alert Alert { get; set; }
/// <summary>
/// [IOS] badge value (can accept string representaion of number or "Increment")
/// </summary>
[JsonProperty("badge")]
public Int32? Badge { get; set; }
/// <summary>
/// [IOS] The name of sound to play
/// </summary>
[JsonProperty("sound")]
public String Sound { get; set; }
/// <summary>
/// [IOS>=7] Content to download in background
/// </summary>
/// <remarks>
/// Set 1 for silent mode
/// </remarks>
[JsonProperty("content-available")]
public Int32? ContentAvailable { get; set; }
/// <summary>
/// [IOS>=8] Category of interactive push with additional actions
/// </summary>
[JsonProperty("category")]
public String Category { get; set; }
/// <summary>
/// [Android] Used for collapsing some messages with same collapse_key
/// </summary>
[JsonProperty(PropertyName = "collapse_key")]
public String CollapseKey { get; set; }
/// <summary>
/// [Android] This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline.
/// The maximum time to live supported is 4 weeks, and the default value is 4 weeks.
/// </summary>
/// <value>
/// Time_to_live value of 0 means messages that can't be delivered immediately will be discarded
/// </value>
[JsonProperty("time_to_live")]
public Int32 TimeToLive { get; set; }
/// <summary>
/// [Android] Uri of activity to open when push activated by user
/// </summary>
[JsonProperty("url")]
public String Url { get; set; }
/// <summary>
/// Payload for push
/// </summary>
[JsonProperty("data")]
public Payload Payload { get; set; }
}
with message builder which serialize your message body to correct json string.

'RestClient' does not contain a definition for 'JsonSerializer' and no extension method 'JsonSerializer'

As i read readme.txt for latest RestSharp:
*** IMPORTANT CHANGE IN RESTSHARP VERSION 103 ***
In 103.0, JSON.NET was removed as a dependency.
If this is still installed in your project and no other libraries depend on
it you may remove it from your installed packages.
There is one breaking change: the default Json*Serializer* is no longer
compatible with Json.NET. To use Json.NET for serialization, copy the code
from https://github.com/restsharp/RestSharp/blob/86b31f9adf049d7fb821de8279154f41a17b36f7/RestSharp/Serializers/JsonSerializer.cs
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new YourCustomSerializer();
The default Json*Deserializer* is mostly compatible, but it does not support
all features which Json.NET has (like the ability to support a custom [JsonConverter]
by decorating a certain property with an attribute). If you need these features, you
must take care of the deserialization yourself to get it working.
Im already installed Newtonsoft.Json with nu package manager, and im trying to register Json.NET to client variable, but didn't work. here is my code:
private void Form1_Load(object sender, EventArgs e)
{
var client = new RestClient("http://homestead.app/vendor");
client.JsonSerializer = new JsonSerializer(); <-- HERE IS THE ERROR
var request = new RestRequest("", Method.GET);
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
textBox1.Text = content;
}
the client.JsonSerializer property is not available.
Please kindly help me. thanks
The serializers for RestSharp must implement two interfaces:
RestSharp.Serializers.ISerializer
RestSharp.Serializers.IDeserializer
You must wrap the serializer from Newtonsoft to type with these interfaces.
There is working code from one project I worked on:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
/// <remarks>
/// Based on http://blog.patrickmriley.net/2014/02/restsharp-using-jsonnet-serializer.html
/// </remarks>
public class RestSharpJsonNetSerializer : RestSharp.Serializers.ISerializer, RestSharp.Deserializers.IDeserializer
{
private readonly JsonSerializer serializer;
/// <summary>
/// Default serializer
/// </summary>
public RestSharpJsonNetSerializer()
{
this.ContentType = "application/json";
this.serializer = new JsonSerializer
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public RestSharpJsonNetSerializer(JsonSerializer serializer)
{
this.ContentType = "application/json";
this.serializer = serializer;
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize></param>
/// <returns>JSON as String</returns>
public string Serialize(object obj)
{
using (var stringWriter = new StringWriter())
{
using (var jsonTextWriter = new JsonTextWriter(stringWriter))
{
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
this.serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
public T Deserialize<T>(RestSharp.IRestResponse response)
{
using (var strReader = new StringReader(response.Content))
{
using (var jsonReader = new JsonTextReader(strReader))
{
var data = this.serializer.Deserialize<T>(jsonReader);
return data;
}
}
}
}
The new readme.txt is updated at least in GitHub but the package still contains the old code.
So the answer of #TcKs is correct:
var request = new RestRequest();
request.JsonSerializer = new Shared.JsonSerializer();
var client = new RestClient();
client.Post(request);
It will be easy if you using mapper object when return
public YourResultBase Login(string username, string password)
{
var client = new RestClient("apiUrl");
var request = new RestRequest(Method.GET)
{
OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; }
};
request.AddHeader("Cache-Control", "no-cache");
IRestResponse<YourResultBase> response = client.Execute<YourResultBase>(request);
var result = response.Data;
return result;
}

RestSharp deserialization of Enum Type Property

I have an object
var testTcc = new TrendingConfigurationConfigDto
{
TrendingConfigurationId =1,
ConfigId = 1,
DeviceId = 1,
Selected = true,
YAxisPosition = YAxisPosition.Left,
Order = 1,
Color = "#ffffff",
Configuration = new BO.Shared.Dtos.List.ConfigurationListDto
{
Id = 1,
Name = "configuration",
Alias = "configuationAlias",
EnableEdit = true,
IsBusinessItem = true
},
Device = new BO.Shared.Dtos.List.DeviceListDto
{
Id = 1,
Name = "Device"
}
};
when I serialized it into json as
var jsonTcc = SimpleJson.SerializeObject(testTcc);
it returned string containing json object with YAxisPosition = 1, and when I tried deserializing it using
testTcc = SimpleJson.DeserializeObject<TrendingConfigurationConfigDto>(jsonTcc);
It is giving an exception System.InvalidCastException with message 'Specified cast is not valid'.
I tried changing YAxisPosition value in json string to string "1" or "Left" it was always giving me the same error, until I removed the property YAxisPosition from json string.
I might be missing some thing (an Attribute on enum property or something similar).
Please help me finding a way so that I can Serialize and De-serialize an object which contains Enum type property, using RestSharp.
Note: I tried successful serialization and de-serialization using NewtonSoft. but I do not want a dependency of my Web API Client on NetwonSoft, as I am already using RestSharp.
RestSharp removed JSON.NET support in v103.0. The default Json Serializer is no longer compatible with Json.NET. You have a couple of options if you want to continue using JSON.NET and maintain backwards compatibility. Beyond that, JSON.NET has more capability and may solve your issues over using the basic .NET serializer which RestSharp now depends on.
Plus you can use the [EnumMember] properties to define custom mappings during deserialization.
Option 1: Implement a custom serializer that uses JSON.NET
To use Json.NET for serialization, copy this code:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
public class JsonNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new JsonNetSerializer();
Option 2: Use a nuget package to use JSON.NET
Instead of doing all of that and having a custom JSON serializer spread throughout your projects is to just use this nuget package: https://www.nuget.org/packages/RestSharp.Newtonsoft.Json. It allows you to use an inherited RestRequest object that defaults to using Newtonsoft.JSON internally like this:
var request = new RestSharp.Newtonsoft.Json.RestRequest(); // Uses JSON.NET
The other option is to set it on every request like this:
var request = new RestRequest();
request.JsonSerializer = new NewtonsoftJsonSerializer();
Disclaimer: I created this project after getting frustrated with having a custom serializer laying around in my projects. I created this to keep things clean and hopefully help others who want backwards compatibility with their RestSharp code that worked prior to v103.
Found solution for this problem:
private IRestClient GetRestClient()
{
return new RestClient(url)
.AddDefaultHeader("Authorization", $"Bearer {token.AccessToken}")
.AddDefaultHeader("Accept", "*/*")
.AddDefaultHeader("Accept-Encoding", "gzip, deflate, br")
.AddDefaultHeader("Connection", "close")
.UseSystemTextJson(new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
});
}
I.e. instructed RestSharp to use System.Text.Json serializer and then instructed the serializer to use JsonStringEnumConverter class to serialize/deserialize enums.
I got it working with the help of PocoJsonSerializerStrategy. RestSharp allows you to specify your own serialization/deserialization strategy, so I created my own strategy that handles enums for me:
public class HandleEnumsJsonSerializerStrategy : PocoJsonSerializerStrategy
{
public override object DeserializeObject(object value, Type type)
{
if (type.IsEnum)
return Enum.Parse(type, (string)value);
else
return base.DeserializeObject(value, type);
}
}
Now you can pass an object of this class to SimpleJson.DeserializeObject call, like this and your enums are handled elegantly:
SimpleJson.DeserializeObject<JsonObject>(Response.Content, Strategy);
If you are using Enumeration class pattern you can user a JsonConverter approach. It means you need to create a custom converter and use JsonConverter attribute on the property you need to serialize. Example is following:
using Newtonsoft.Json;
using System;
namespace MyAwesomeAPI
{
public class EnumerationConverter<TEnum> : JsonConverter
where TEnum : Enumeration
{
public override bool CanConvert(Type objectType) => objectType == typeof(TEnum);
public override bool CanRead { get => true; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var uknown = Enumeration.FromDisplayName<TEnum>("Unknown");
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
var enumText = reader.Value?.ToString();
if (string.IsNullOrEmpty(enumText))
{
return uknown;
}
try
{
return Enumeration.FromDisplayName<TEnum>(enumText);
}
catch (Exception)
{
return uknown;
}
}
return uknown;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
}
Where our enum looks something like:
public class CardType : Enumeration
{
public static CardType Unknown = new(0, nameof(Unknown));
public static CardType Amex = new(1, nameof(Amex));
public static CardType Visa = new(2, nameof(Visa));
public static CardType MasterCard = new(3, nameof(MasterCard));
public CardType(int id, string name) : base(id, name)
{
}
}
And last thing is apply the attribute to a property
public class CreditCardResponse
{
[JsonProperty("card_number")]
public string CardNumber { get; set; }
[JsonProperty("card_type")]
[JsonConverter(typeof(EnumerationConverter<CardType>))]
public CardType CardType { get; set; }
}

Categories

Resources