I am working on a project and trying to integrate SignalR into it. I have a class with a data contract which allows me to use an underscore separated format on my client, and standard Pascal Case on the server, something like this:
[DataContract]
public class Foo {
[DataMember(Name = "first_name")]
FirstName { get;set; }
[DataMember(Name = "last_name")]
LastName { get;set; }
[DataMember(Name = "phone")]
Phone { get;set; }
}
This works fine when passing data through a fetch command to the Razor Page OnGet and OnPost methods, but does not work when using SignalR.
When sending data to server via SignalR, first_name and last_name are null, while phone gets sent correctly.
How can I make SignalR respect the DataMember Name when serializing/deserializing?
I was able to resolve this issue by using the Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson package and adding the following in Startup.cs
services.AddSignalR()
.AddNewtonsoftJsonProtocol();
I suppose currently you are sending the data as an object, somehow like this:
Foo foo = new Foo();
_hub.Clients.All.InvokeAsync("ReceiveMessage", foo);
What I suggest is that you create the JSON by hand on the server side and send that, because you have better controll over the property names that way.
Add the JsonProperty attribute to the Foo class:
[DataContract]
public class Foo {
[DataMember(Name = "first_name")]
[JsonProperty("first_name")]
FirstName { get;set; }
[DataMember(Name = "last_name")]
[JsonProperty("last_name")]
LastName { get;set; }
[DataMember(Name = "phone")]
[JsonProperty("phone")]
Phone { get;set; }
}
And when you send the message, send the JSON string instead of letting SinglaR parse your object:
Foo foo = new Foo();
string json = JsonConvert.SerializeObject(foo);
_hub.Clients.All.InvokeAsync("ReceiveMessage", json);
The fields are not being serialized because their serialization attributes ([DataMember]) are not compatible with SignalR's default serialization engine (System.Text.Json).
One possible solution is to switch the serialization engine to Newtonsoft's JSON implementation, as suggested in #Aleksandr Albert's answer. Another solution is to use System.Text.Json.Serialization.JsonIncludeAttribute instead, e.g.
public class Foo {
[JsonPropertyName("first_name")]
[JsonInclude]
FirstName { get;set; }
[JsonPropertyName("last_name")]
[JsonInclude]
LastName { get;set; }
[JsonPropertyName("phone")]
[JsonInclude]
Phone { get;set; }
}
Each solution is (in)compatible with one of the serialization engines.
Related
I am using following code in ASP.NET Web API application.
//Support camel casing
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
When returning JSON via POCO or DataTable, it converts property name in camel casing.
Assume My class has two properties.
Class Obj{
public string DataBase{ get; set; }
public string ChangedBy { get; set; }
}
When I return any object of this class, I will get JSON like this:
{
"dataBase":"Oracle",
"changedBy":"XYZ"
}
It seems issue is when you have '_' in the property name. CamelCasing is not making sense.
My class has columns like this:
DATA_BASE
CHANGED_BY
Now, I am receiving JSON like this:
{
"datA_BASE":"Oracle",
"changeD_BY":"XYZ"
}
I was expecting:
{
"dATA_BASE":"Oracle",
"cHANGED_BY":"XYZ"
}
Sorry for the (maybe) trivial question but, I'm trying to consume a web service where the entities and my data model classes are named different.
I want to keep my model .Net Class name and use a Json Attribute name to map, serializer/deserializer, with the corresponding web service entity.
For example:
Web Service Entity:
"People"
My Model Class:
"Employee"
What I've already do:
[JsonObject(Title="People")]
public class Employee
{
[JsonProperty("DifferentPropertyName")]
string propertyName1 { get; set; }
}
But the json serializer/Deserializer continues to use the .Net class name and I need to set the jsonObject Title.
There is a way to achieve it?
EDIT
I'm working on a Xamarin Forms app, using Simple.OData.Client to consume an OData Service
Thanks
DataContractAttribute may be your solution.
public class RichFilter
{
public Trolo item { get; set; }
}
[DataContract(Name = "item")]
public class Trolo
{
public string connector { get; set; }
}
If you serialize a RichFilter object, here is the output :
{"item":{"connector":"AND"}}
I'm having a hard time figuring something out that seems as a "easy" problem.
I'm working with Microsoft Azure mobile apps .Net backend, a MSSQL database, Entity Framework code-first and AutoMapper.
So i have the following objects:
public class Route
{
public string Id { get; set; }
[...] //some other properties
public string SerializedGoogleRoute { get; set; }
}
public class DtoRoute
{
public string Id { get; set; }
[...]
public DtoGoogleRoute GoogleRoute { get; set; }
}
public class DtoGoogleRoute
{
[...] //only strings, ints,...
}
So what I want to do is: In the database save the GoogleRoute as a serialized string because it consists of many properties and I don't need them in different columns - I just want it as a serialized string in one column on the route entity.
When the Route object is projected to the DtoRoute object I want the GoogleRoute to be serialized and vice versa.
Because I'm working with LINQ / queryables I am limited to a few AutoMapper mapping options (see AutoMapper wiki). And with none of these I can't get it to work.
The problems I'm facing/what I tried:
I can't serialize/deserialize the string to the DtoGoogleRoute on mapping (with MapFrom or ConstructProjectionUsing) because LINQ obviously cannot transform the JsonConvert.Serialize/Deserialize methods to SQL statements.
I tried having a DtoGoogleRoute property in the Route object and a string property in the DtoRoute object with getters/setters doing the (de)serialization. This works almost perfectly in a custom API controller but because of the OData query filter the azure mobile app .Net backend uses in the tablecontrollers again only the serialized string property gets returned to the client (because OData/LINQ does not know of the other property).
Another option was making a complex type out of DtoGoogleRoute with Entity Framework - this works fine but not with AutoMapper because AutoMapper can't handle complex types.
For now I'm working with a custom API controller and this works. But it would be better to use the tablecontrollers because they support offline sync.
I can't imagine such a simple thing (at least I thought it was a simple thing) can't be done or is so hard to do. But maybe the problem is all the components (tablecontroller, OData, LINQ, EF, AutoMapper) involved.
I would really be thankful if someone could help.
[EDIT]: I think the fact that it works with a normal api controller and not with a tablecontroller has something to do with OData. I tried putting the same code in a tablecontroller method and in an API controller method. when calling the API controller method I can see on the server that it just calls this function and returns all the right properties to the client (checked with fiddler). But when calling the tablecontroller method the tablecontroller method "rewrites" the URL to a OData URL --> I think this is because of some of the EnableQuery or other OData attributes. Because here (although not AutoMapper but it seems like a similar project from Microsoft) it says that the EnableQuery attribute is called twice - also when the request leaves the server. And I think it cuts of the GoogleRoute property because it does not know about this property in the OData metadata or something like that.
You can achieve it like this -
internal class RouteToDtoConverter : TypeConverter<Route, DtoRoute>
{
protected override DtoRoute ConvertCore(Route source)
{
return new DtoRoute
{
Id = source.Id,
GoogleRoute = JsonConvert.DeserializeObject<DtoGoogleRoute>(source.SerializedGoogleRoute)
};
}
}
internal class DtoToRouteConverter : TypeConverter<DtoRoute, Route>
{
protected override Route ConvertCore(DtoRoute source)
{
return new Route
{
Id = source.Id,
SerializedGoogleRoute = JsonConvert.SerializeObject(source.GoogleRoute)
};
}
}
public class Route
{
public string Id { get; set; }
public string SerializedGoogleRoute { get; set; }
}
public class DtoRoute
{
public string Id { get; set; }
public DtoGoogleRoute GoogleRoute { get; set; }
}
public class DtoGoogleRoute
{
public int MyProperty { get; set; }
public int MyProperty2 { get; set; }
}
AutoMapper.Mapper.CreateMap<Route, DtoRoute>()
.ConvertUsing(new RouteToDtoConverter());
AutoMapper.Mapper.CreateMap<DtoRoute, Route>()
.ConvertUsing(new DtoToRouteConverter());
var res = Mapper.Map<DtoRoute>(new Route
{
Id = "101",
SerializedGoogleRoute = "{'MyProperty':'90','MyProperty2':'09'}"
});
var org = Mapper.Map<Route>(res); //pass
I'm using Restsharp to deserialize some webservice responses, however, the problem is that sometimes this webservices sends back a json response with a few more fields. I've manage to come around this so far by adding all possible field to my matching model, but this web service will keep adding/removing fields from its response.
Eg:
Json response that works:
{
"name": "Daniel",
"age": 25
}
Matching model:
public class Person
{
public string name { get; set; }
public int age { get; set; }
}
This works fine: Person person = deserializer.Deserialize<Person>(response);
Now suppose the json response was:
{
"name": "Daniel",
"age": 25,
"birthdate": "11/10/1988"
}
See the new field bithdate? Now everything goes wrong. Is there a way to tell to restsharp to ignore those fields that are not in the model?
If there's that much variation in the fields you're getting back, perhaps the best approach is to skip the static DTOs and deserialize to a dynamic. This gist provides an example of how to do this with RestSharp by creating a custom deserializer:
// ReSharper disable CheckNamespace
namespace RestSharp.Deserializers
// ReSharper restore CheckNamespace
{
public class DynamicJsonDeserializer : IDeserializer
{
public string RootElement { get; set; }
public string Namespace { get; set; }
public string DateFormat { get; set; }
public T Deserialize<T>(RestResponse response) where T : new()
{
return JsonConvert.DeserializeObject<dynamic>(response.Content);
}
}
}
Usage:
// Override default RestSharp JSON deserializer
client = new RestClient();
client.AddHandler("application/json", new DynamicJsonDeserializer());
var response = client.Execute<dynamic>(new RestRequest("http://dummy/users/42"));
// Data returned as dynamic object!
dynamic user = response.Data.User;
A simpler alternative is to use Flurl.Http (disclaimer: I'm the author), an HTTP client lib that deserializes to dynamic by default when generic arguments are not provided:
dynamic d = await "http://api.foo.com".GetJsonAsync();
In both cases, the actual deserialization is performed by Json.NET. With RestSharp you'll need to add the package to your project (though there's a good chance you have it already); Flurl.Http has a dependency on it.
I have a WCF service that returns some JSON from a serialized object:
public class Response
{
public string Token { get; set; }
public int UserId { get; set; }
...
}
I've added some extra properties to this class, but now some of the implementations fails because they read it like:
string[] ResultLoginValues = e.Result.ToString().Split(',');
and it's returned in alphabetically order instead of the old order with the new properties last.
Is there any way I can change the order, or should they rewrite the clients?
For solution refer WCF DataContract DataMember order