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"}}
Related
I am using .NET Framework and ASP.NET Core to create a REST web Api.
This web api has a call that gets a request model to save data and some call that later retrieves the data.
Most of the data is structured information I need in the backend and it is saved into different fields and tables in the database. On retrieval it is loaded from those tables and returned.
This all works.
However, I now have a requirement where the caller wants to save and later retrieve arbitrary data (lets just say a random json) as one of those fields. I can save and load json from the database that is not a problem, my problem is to build the web api model for my request.
[HttpPost]
public IActionResult Save([FromBody] ApiCallRequestModel request)
{
// ...
}
public sealed class ApiCallRequestModel
{
// structured, well known information
public int? MaybeSomeNumber { get; set; }
[Required]
public string SomeText { get; set; }
[Required]
public SubModel SomeData { get; set; }
// one field of unknown json data
public ??? CustomData { get; set; }
}
I could think of dynamic or maybe even ExpandoObject or JObject to try and I might, but I would like a solution that works because it's best practice, not just because I tried and it didn't fail today with my simple tests.
If everything else fails, I could just make the field a string and tell the client to put serialized json into it. But that's a workaround I would see as a last resort if this question yields no answers.
It has proven to be extremly hard to google this topic, since all words I would use lead me to pages explaining Json serialization of my request model itself. I know how that works and it's not a problem. The mix of structured data and free json is what I cannot find out from a somewhat authorative source.
So what type would you use here, what is the best practice for receiving arbitrary json in one property of your model?
So to sum this up, as suggested I used a JToken from the Json.NET nuget package, since I already had that package in my project.
[HttpPost]
public IActionResult Save([FromBody] ApiCallRequestModel request)
{
// ...
}
public sealed class ApiCallRequestModel
{
// structured, well known information
public int? MaybeSomeNumber { get; set; }
[Required]
public string SomeText { get; set; }
[Required]
public SubModel SomeData { get; set; }
// one field of unknown json data
public JToken CustomData { get; set; }
}
Works like a charm.
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.
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've an WebAPI OData v3 interface using ODataConventionModelBuilder. It contains some entities which are inherited, and also a model which has a collection of the abstract object:
public abstract class BaseObject
{
[Key]
public int Id { get; set; }
[ForeignKey("Object3")]
public int? ParentId { get; set; }
public virtual Object3 Parent { get; set; }
}
public class Object1: BaseObject
{
}
public class Object2: BaseObject
{
}
public class Object3
{
[Key]
public int Id { get; set; }
public ICollection<BaseObject> MyObjects { get; set; }
}
I'm calling the interface using Breeze with client side metadata, using expand:
http://example.com/api/Object3?$expand=MyObjects
The server response looks like this:
{
"odata.type":"MyNamespace.Object1",
"odata.id":"http://example.com/api/BaseObject(1)",
"Parent#odata.navigationLinkUrl":"http://example.com/api/BaseObject(1)/Parent",
"Id":1,
"ParentId":1
}
Breeze now recognizes this as an entity of type Object1. But if I modify the entity and save the changes it makes a POST request to http://example.com/api/BaseObject(1). To being able to handle the different concrete types I need the POST request to go to the specific controller http://example.com/api/Object(1).
What do I need to change so that Breeze makes to update POST calls to the concrete controller and not the controller of the base object?
UPDATE: After inspecting the Breeze source code, it seems like Breeze uses the odata.id as URI for the POST request. Is it somehow possible to have the OData API return the URI for the concrete object as odata.id instead of the base object?
I got this working with a nasty hack by removing extraMetadata from all entities before saving with breeze:
var entities = manager.getEntities(null, breeze.EntityState.Modified);
for (var i = 0; i < entities.length; i++) {
delete entities[i].entityAspect.extraMetadata;
}
It there are no extraMetadata (which contains the odata.id) are available, breeze calculates the URI to the controller of the concrete model.
I don't know if there's a better solution available, that the OData API sends the correct odata.id in the first place.
My web api is returning a set of objects which are differ from the Domain object. Forexample, I my domain has an Employee class but I don't want to expose all the members of the Employee class in my api so I created another class called EmployeeApiModel.
Now my WebApi is returning a List of EmployeeApiModel but I want to be able to specify the name to which it should serialize to. That is instead of <EmployeeApiModel> tag in the xml, I want to get <Employee> but without changing the fact that the underlying class which is being serialized is EmployeeApiModel.
How can I achieve this?
Technically, Web Api support both json and xml based on content negotiation mechanism, Json is the default format, if you want to receive xml, just put on header:
Accept: application/xml
To understand more content negotiation, access this
Since you want your api support both json and xml, you should use DataContract and DataMember Attribute for serialization for your model: EmployeeApiModel, something like:
[DataContract(Name = "Employee")]
public class EmployeeApiModel
{
[DataMember(Name = "Name2")]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
See more on this blog-post
You can control the output of your serialized XML by using various Attribute tags.
[XmlRoot("Employee")]
Public class EmployeeApiModel
{
[XmlElement("fname")]
public string FirstName { get; set; }
public string LastName { get; set; }
public int age { get; set; }
}
this will produce XML like:
<Employee>
<fname>John</fname>
<LastName >Smith</LastName >
<age>24</age>
</RootElementsName>
You can read more about the various XML modifiers here: http://msdn.microsoft.com/en-us/library/e123c76w.
If you want to use existing XML modifiers for JSON, check out this post: Serialize .Net object to json, controlled using xml attributes