Extending the RestSharp serialization with a custom serializer issue - c#

I'm trying to improve the default RestSharp serialization by using Json.net library. In order to customize the serialization you have to implement ISerializer interface:
public class LowerCaseSerializer : ISerializer{
public LowerCaseSerializer(){
ContentType = "application/json";
}
public string Serialize(object obj){
var settings = new JsonSerializerSettings{
ContractResolver = new LowerCaseResolver()
};
return JsonConvert.SerializeObject(obj, Formatting.None, settings);
}
string ISerializer.RootElement { get; set; }
string ISerializer.Namespace { get; set; }
string ISerializer.DateFormat { get; set; }
public string ContentType { get; set; }
}
As you see I'm also extending the ContractResolver. This is the actual code that does the lowercasing:
public class LowerCaseResolver : DefaultContractResolver{
protected override string ResolvePropertyName(string propertyName){
return propertyName.ToLower();
}
}
Once all this is setup I can use it with RestSharp:
var request = new RestRequest(url, method);
if (ShouldAddBody(method)){
request.JsonSerializer = new LowerCaseSerializer();
request.AddObject(body);
}
var response = client.Execute<T>(request);
Everything works, except the properties are not in lower case. When debugging the debuger goes into the Constructor of the serializers, but it's method is never called. When I tried exactly the same for deserializations (IDeserialize interface, which attaches to the client) the method for lower casing was called for each property.
What I have also tried:
request.RequestFormat = DataFormat.Json; // no change
// this correctly lower cases the properties
var json = new LowerCaseSerializer().Serialize(body);
// wrong request sent to the server, it tells me that some property is missing
request.AddBody(json);
// the exact same thing with this
request.AddParameter("application/json", json, ParameterType.RequestBody);
The thing I noticed with the last two: if I have lower case properties and let RestSharp serializes then the request has 5 parameters (one for each property). If I add it via upper two methods it has only one property and that's the whole json.
I check the RestSharp issues to no avail. Any suggestions?
Update:
This very strange:
forked the RestSharp, installed Json.net, works fine
copied RestRequest class from RestSharp fork, pasted to my application, used it, works fine
started new project, installed RestSharp and Json.net via Package manager, works fine
Then removed all packages from my main application, redownloaded, doesn't work. Kinda giving up on this.
Update 2:
Debugging through the forked version I noticed this: The RequestFormat has to be Dataformat.Json or it will use Xml serializer. But that doesn't fix the problem. I tried setting both (there are only two) serializers to null:
request.JsonSerializer = null;
request.XmlSerializer = null;
In the new project I did this causes NullReferrenceException as expected. But in the old one nothing happens. I tried renaming the variables, making another variable of the same type, but nothing fixes is. It just seems that in the project I have the RestRequest class is somehow bugged.
I also added a new project to the solution. And even there the code works. So it's just one project that has the problem.

Since you can't reproduce it in a new project, there must be something different going on in this particular project, that's causing the issues, you're describing.
A couple of things you could try (in no particular order):
Check that you're using the exact same version of the library in both projects (the one that works and the one that doesn't): package version and target platform (net4, net35...).
Delete the packages folder of your non-working project so that NuGet will be forced to re-download all the packages.
Lookup the exact path to the referenced library in Visual Studio Properties window when you have RestSharp from References node selected. Do a binary compare between the libraries referenced by the working and the non-working project.
Unfortunately there's no symbol package for RestSharp on SymbolSource, so you can't directly debug RestSharp in your non-working project. You could use Reflector.NET VSPro if you have the license or haven't used the trial before.
Move parts of your non-working project to the working one until it stops working.
EDIT:
Looking at the source code of RestRequest, AddObject doesn't seem to use the JsonSerializer you are setting. Have you tried using AddBody instead?

Related

Specifying JsonSerializerOptions in .NET 6 isolated Azure Function

I'm trying to call a Web API from code in an Azure Function that I've just ported to .NET 6 (isolated hosting model). I took the chance of the migration to get rid of the RestSharp and Json.NET dependencies, now only just using HttpClient and System.Text.Json for handling the HTTP calls and JSON stuff.
I did try to use this code which seemed like the perfect combo:
Project project = await _httpClient.GetFromJsonAsync<Project>(someUrl);
if (project != null)
{
HttpResponseData callResponse = req.CreateResponse(HttpStatusCode.OK);
await callResponse.WriteAsJsonAsync(project);
return callResponse;
}
The call works fine - I get back my Project object without any hitch.
But unfortunately, with this code, I cannot seem to influence the way the JSON in the response gets rendered - e.g. in my case, null values are returned (which I want to avoid), and all property names are capitalized ("Institute", instead of "institute", "LeadLanguage" instead of "leadLanguage").
No problem - just use a JsonSerializerOptions object and define what you want, I thought. Sure, I can create such an object - but where would I plug that in??
WriteAsJsonAsync doesn't seem to support any serializer options as parameter (why??), and I couldn't find a way to globally define my JsonSerializerOptions (since everything I find seems to be based on the services.AddControllers().AddJsonOptions() method - which I cannot use since my Azure Function doesn't have the AddControllers part in its startup code).
I have managed to get the results I want by doing this:
if (project != null)
{
HttpResponseData callResponse = req.CreateResponse(HttpStatusCode.OK);
callResponse.Headers.Add("Content-Type", "application/json");
string jsonResponse = JsonSerializer.Serialize(project, settings);
await callResponse.WriteStringAsync(jsonResponse, Encoding.UTF8);
return callResponse;
}
but that seems a bit convoluted and "low-level" - manually converting the result object into string, having to manually set the Content-Type and all ....
Is there really no way in an Azure Function (.NET 6 isolated hosting model) to globally specify JsonSerializerOptions - or call WriteAsJsonAsync with a specific serializer options object?
And 10 seconds after I posted the question - of course! - I ran across the way to do it with an Azure Function.
Something like this:
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s =>
{
s.AddHttpClient();
// define your global custom JSON serializer options
s.Configure<JsonSerializerOptions>(options =>
{
options.AllowTrailingCommas = true;
options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.PropertyNameCaseInsensitive = true;
});
Hope that might help someone else down the line!

Why is the Newtonsoft Json serializer ignoring the [JsonIgnore] attribute? [duplicate]

I have this setup
Asp Core 3.1 API
Shared Lib with MyClass that is sent between API and client
Client App with Com classes
On the MyClass that is sent between them I have a field ComField that references a com class, this is only used on the client app and should not be (de)serialized, therefore I have it marked with [JsonIgnore]
class MyClass{
[JsonIgnore]
public ComThingy ComField {
get{// code here that throws the error when deserilaized on the API}
set{// code here}
}
}
When I write the API to accept the class like this, I get an error when the class is deserialized. The debugger throws the error while deserializing the MyClass, before it enters the method:
[HttpPost]
public async Task<ActionResult<MyClassReply>> Post([FromBody] MyClass myclass){
// code here
}
The API throws an exception that accessing the getter on MyClass throws an error (because that Com stuff isn't on the API).
If I deserialize manually it works fine, but then my swagger doesn't generate the whole API correctly.
[HttpPost]
public async Task<ActionResult<MyClassReply>> Post(){
// this works fine
var rdr = new StreamReader(Request.Body);
var mcj = await rdr.ReadToEndAsync();
var myclass = Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(mcj);
// code here
}
So my question is: how come the ASP API builtin deserialization ignores the JsonIgnore attribute and still tries to deal with that property (throwing an error), and why does deserializing manually work as expected (ie ignore that property)? The default pipeline still uses NewtonSoft rght?
And how do I make the default deserialization work correctly?
Starting from ASP.NET Core 3.0, the default JSON serializer is System.Text.Json, and not Newtonsoft.Json. You need to call .AddNewtonsoftJson() in your Startup.cs to use it (see for example this answer).
Your issue might simply be that you're not using the proper JsonIgnore attribute. Both serializers have the same named attribute:
System.Text.Json.Serialization.JsonIgnoreAttribute
Newtonsoft.Json.JsonIgnoreAttribute
Maybe your using statement are importing the Newtonsoft.Json one instead of the System.Text.Json one?

Expect Continue Inconsistencies on Different Environments

I have some troubles with the header Expect 100-Continue on a DELETE method inside a .NET 4.5 application. The client in provides also a small content body inside the call.
The official microsoft documentation seems to imply that this header is passed by default only in PUT and POST calls with a non-empty content, but it does not say nothing about DELETE. Does anyone know if this configuration also applies to DELETE?
I'm experiencing different behaviour on different environment of my web application in particular I have some environments in which the header is never passed (even in PUT and POST) without any clue about it being deactivated.
In order to interact with this option I know only four ways:
Via ServicePointManager through
System.Net.ServicePointManager.Expect100Continue = false;
Editing client options
var c = new HttpClient();
c.DefaultRequestHeaders.ExpectContinue = false;
Adding or removing it manually on HttpWebRequest with AddHeader/Remove
Using the following property on Web.Config
<system.net>
<settings>
<servicePointManager expect100Continue="false"/>
</settings>
</system.net>
Unfortunately none of these seems to be my case. Are there any other ways to mess with this option?
Just managed to find out the solution for this issue. I found out that some of my environment use the couchbase client to handle distributed cache, differently from IIS couchbase internally sets Expect100Continue to true but it does that in the following way.
namespace Couchbase.Configuration.Client
{
public class ClientConfiguration {
//...
public ClientConfiguration()
{
//...
this.Expect100Continue = false;
}
//--
public bool Expect100Continue
{
get
{
return ServicePointManager.Expect100Continue;
}
set
{
ServicePointManager.Expect100Continue = value;
}
}
}
}
they use change this property using the static ServicePointManager method and this means that every following instantiated client will change their behaviour.
My version of Couchbase Client is 2.1.4.0

Why the result from the web service references and Service references are different?

I am bit curious about one thing which has happen while trying to understand the concept of Service References and Web Service References.
What I did is?
In my project I have added a web service as a Service Reference and trying to get my script run through the use of client.
But while getting result it is throwing an exception as in the following image:
I have tried to trace out the cause but not able to get the proper answer for that.
I have following code for the resultant object.
[
ComVisible(false),
Serializable,
SoapTypeAttribute("RecordList", "http://www.someadd.com/dev/ns/SOF/2.0"),
XmlType(TypeName="RecordList", Namespace="http://www.someadd.com/dev/ns/SOF/2.0")
]
public class MyRecordListWrapper
{
private IxRecordList recordList = null;
private const string XMLW3CSchema = "http://www.w3.org/2001/XMLSchema";
[SoapElement("Headers")]
public Header[] Headers = null;
[SoapElement("Records")]
public Record[] Records = null;
// some methods to work on intialization
public SmRecordListWrapper(ref IxRecordList p_RecordList)
{
recordList = p_RecordList;// record list initialization
Headers = CreateWrapperHeaders(); // will return header class object
Records = CreateWrapperRecords(); // will return record object
}
}
Can anyone tell me why this error is showing for me?
While adding reference as a Web Service Reference
when I add the same reference as a web reference that time the program is not showing any error and runs successfully?
So can anyone tell me what is the difference in working with the same code using service reference and web service reference?
and Which is a correct way to ass references?
Hope I will get some more described answers to make the things easy to understand.
Thanks in advance.
Adding a web reference, visual studio uses xsd.exe to generate the classes from the service metadata. This uses XmlSerializer under the hood.
Adding a service reference, visual studio uses svcutil.exe to generate the classes from the metadata. This uses DataContractSerializer under the hood.
Two separate tools, two outcomes. For general information, DataContractSerializer is a lot less forgiving when it comes to generating classes from metadata.

Xamarin trying to get users from webservice, immediate crash

Hi again stackoverflow,
I am following a tutorial on how to build an Android application in Xamarin and I have encountered an error I cannot resolve on my own.
Hoping anyone of you might shed some light on how to proceed from here:
This code is copied from the tutorial itself (source: pluralsight)
private JsonServiceClient client;
private IList<User> users;
void PopulateSelectUsers ()
{
var response = client.Get(new Users());
users = response.Users.ToList ();
var names = users.Select (u => u.Name);
var usersSpinner = FindViewById<Spinner> (Resource.Id.usersSpinner);
usersSpinner.Adapter = new ArrayAdapter<string> (this, Android.Resource.Layout.SimpleListItem1, names.ToArray ());
}
Where "Users" is a request:
public object Get(Users request)
{
return new UsersResponse { Users = Repository.GetUsers() };
}
[Route("/users", "GET")]
public class Users : IReturn<UsersResponse>
{
}
public class UsersResponse
{
public IEnumerable<User> Users { get; set; }
}
HOWEVER once Xamarin read this line of code:
var response = client.Get(new Users());
then the application in the emulator just crashes and Xamarin leaves me no information on what happened or how to fix it..
It seems that this code works in the tutorial and as I mentioned before, Xamarin leaves me no information on what happened or how to fix it, so my question would be if perhaps one of you know what Is happening or perhaps a way to fix it.
Also perhaps worth mentioning is that I'm using redis to store users.
IF you want to view the userservice it is available here:
http://shan13alwo.cloudapp.net/api/metadata
You can check if code works by sending JSON GET to http://shan13alwo.cloudapp.net/api/users
Thank you in advance,
UPDATE:
I realize I might have been unclear of what I wanted to do but to simplify. What I want to do Is get my users(ienumerable) from my redis database and store them in a List. Using the this code in Xamarin does not work:
var response = client.Get(new Users());
users = response.Users.ToList ();
As Scott previously pointed out to me In another topic made here on Stackoverflow:
ServiceStack v4 was JUST released and using different versions of the servicestack library in Xamarin and Visual Studio resulted in this strange behavior.
Reverting back to V3 of ServiceStack solved my issues.
I would like to thank Scott for your assistance and would like to ask one last thing:
Where can I download the VERSION 3, free license version of the ServiceStack libraries for Android?
(Think its called "AndroidIndie")
Downloading and compiling the solution with the libraries from the lib folder of ServiceStack results in "evaluation software, build valid for 24 hours".
Thank you in advance.

Categories

Resources