I have an API that returns some data of a class in JSON. Is there any way to return only some specific fields of a C# class in JSON?
For example:
class Person {
public int Id{ get; set; }
public string Name { get; set; }
public string Family { get; set; }
public string Gender { get; set; }
}
Person myPerson = new Person();
var Json = (new
{
Person = myPerson
});
return Request.CreateResponse(HttpStatusCode.OK, Json);
It returns ID, Name, Family, Gender. I need to return only the Name and Family. I thought I can create an object and add my specific fields in the class in that object and return object?
Use anonymous types?
return Request.CreateResponse(HttpStatusCode.OK, new {Name = person.Name; Family = person.Family});
You should consider applying the DRY principle.
The DRY principle is not "never write code twice", but rather that "every piece of knowledge must have a single, unambiguous, authoritative representation in the system".
What this means is that you have to build a model that has a meaningful name, contains properties that it actually has, and can be changed as needed later without having to figure out where else in the application it's used. I'm certain you could also find a name that would better represent this area of the application.
Should I create a different model with just the properties I need or
use the same model and have NULL values for the field I don't use? Or just create anonymous type and make this way harder in future?
I don't recommend using the same model for your case, from my understanding of it. It's going to make your life easier down the road if you build a model that has a meaningful name as I said before.
So what should we do? I have seen many people trying to use JsonIgnore in domain model but you should not get down that path. You should avoid using JsonIgnore in domain model. I will give an example for it.
For example:
class Person {
public int Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
public string Gender { get; set; }
}
So you have this model and you want other models to inherit from it but like in your case you don't want to return Id and Gender, only Name and Family. So you go like this.
class Person {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Family { get; set; }
public virtual string Gender { get; set; }
}
public class PersonNameModel : Person {
[JsonIgnore]
public override int Id { get; set; }
[JsonIgnore]
public override string Gender{ get; set; }
}
If a class has many properties and you only want to serialize a small
subset of them then adding JsonIgnore to all the others will be
tedious and error prone. The way to tackle this scenario is to add the
DataContractAttribute to the class and DataMemberAttributes to the
properties to serialize. This is opt-in serialization, only the
properties you mark up with be serialized, compared to opt-out
serialization using JsonIgnoreAttribute.
But, you can do it with the help of JsonIgnore but this way does not support XML format. So, in case your application has to support XML format more (or only support XML), instead of using Json.Net, you should use [DataContract] which supports both JSON and XML.
Datacontract is a great solution. It gives me a clean REST API. At the same time when I save the data in a no-sql, the ignored properties are persisted despite the objects being stored as json.
[DataContract]
public class Person {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Family { get; set; }
public virtual string Gender { get; set; }
}
public class PersonNameModel : Person {
// included in JSON
[DataMember]
public override string Name { get; set; }
[DataMember]
public override string Family { get; set; }
// other inherited properties will be ignored
}
I will also mention another way of doing it as there are more (custom contact resolver, Web API convention ("ShouldSerialize")...)
Another way you can do it is to decorate your auto property with JsonProperty attribute in order to skip the serialization of that field if it is null.
Example:
class Person {
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Gender { get; set; }
}
Happy coding!
Related
I have not worked with EF for a while.
As an exercise, I am writing a core web api that allows keeping track of a user medias.
A media can be a postcard, a photo album, a recording, a book...
I would like to know what is the way to go/best practice in writing the Add (createMedia) method:
[HttpPost]
public async Task<ActionResult<bool>> Add(Media media)
My model is comprised of several specific classes representing one type of media - like Postcard, Photoalbum, recording, etc. In addition, there is a Media type - which contains shared properties among all media types:
public class Media
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public MediaType Type { get; set; }
public bool InUse { get; set; }
public string Date { get; set; } //yyyy-mm-dd
public string Owner { get; set; }
}
As an example of one of the specific media types:
public class Postcard
{
[ForeignKey("Id")]
public int MediaId { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Place { get; set; }
public string Language { get; set; }
}
I designed my EntityFramework db to consist of a 1-1 relation between Media and the relevant specific media table.
What is the best practice in writing the Add method ? Should it receive a Media object, and based on MediaType create the respective type ? I started with this approach, and had the action receive a second parameter named detailsJson, which I would parse and fill the respective object using reflection, but figured out that POST binding will not bind 2 objects.
I'm not well versed in design patterns.
Should there exist as many AddBook, AddPostcard... as many media types ?
I understand all models should be POCO objects, without inheritance.
I read about DTOs, but does not see how it helps me here.
I guess the Postcard and Book although both media types, they have different properties? for example, From, To, Place fields will not be there in book, whereas Book might have ISBN, Author, Genre etc.
so essentially they are different POCO objects altogether, media type is just a relationship they have in common
In this case, it's OK to have separate Add methods like AddBook, AddPostcard etc.
Update:
The individual POCO objects can refer to Media type to avoid duplicating same properties, same as underlying entity relationship
public class Postcard
{
public Media Media { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Place { get; set; }
public string Language { get; set; }
}
What about having a Key-Value table?
public class MediaValue {
public string MediaId {get;set;}
public string AttributeName {get;set;}
public string AttributeValue{get;set;}
//the key is the couple MediaId - AttributeName
}
public class Media {
//shared properties
public MediaType MediaType {get;set;}
public IEnumerable<MediaValue> Attributes {get;set;}
}
public enum MediaType {
PostCard, PhotoAlbum, ....
}
then you could add the DTOs foreach mediatype
public abstract class MediaDTO {
//shared properties
}
public class PostCardDTO : MediaDTO {
public string PostCardSpecificAttribute {get;set;} // this.PostCardCustomAttribute = aMediaDbObject.Attributes.FirstOrDefault(x => x.AttributeName == "PostCardSpecificAttribute");
}
I'm using Entity Framework Core, and the generated class has its own properties, i.e.
DataModel.Agent.cs
public partial class Agent {
public virtual decimal Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
}
But I need other properties, so I declare them in another file:
Agent.cs
public partial class Agent
{
[NotMapped]
public dynamic Custom { get; set; }
}
The problem is that Agent.cs is compiled before DataModel.Agent.cs, so the compiler generates properties in this order: Custom, Id, Name, and the resulting JSON is weird.
I want it to be: Id, Name, Custom. In other words, I always want the DataModel class to come first.
EDIT: Just to clarify, the only objective is to make the JSON prettier by always putting the Id first, which is a very common pattern. This has absolutely no impact on how the application works.
Is there a way to force the compiler to always compile one of the files first?
Well you really shouldn't count on JSON property order BUT if using json.net
public class Account
{
public string EmailAddress { get; set; }
// appear last
[JsonProperty(Order = 1)]
public bool Deleted { get; set; }
[JsonProperty(Order = 2)]
public DateTime DeletedDate { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
// appear first
[JsonProperty(Order = -2)]
public string FullName { get; set; }
}
http://www.newtonsoft.com/json/help/html/JsonPropertyOrder.htm
For my example, I have two classes
public class Location
{
public int Id { get; set; }
public string Name { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string CountryCode { get; set; }
}
public class Customer : Location
{
public string BankAccountNumber { get; set; }
public string BankSortCode { get; set; }
}
In my query, I am returning all the locations and customers.
http://localhost:80/odata/Location?select=Id,Name,Town
However if I try to select anything in the customer (edit: So I want all locations, but bank account numbers if the location is also a customer), I get an error.
http://localhost:80/odata/Location?select=Id,Name,Town,BankAccountNumber
"The query specified in the URI is not valid. Could not find a property named 'BankAccountNumber' on type 'MyNamespace.Location'."
Is there any way to select the field in inheriting types, without selecting all? Thanks.
According to OData.org, there are 2 options to query a derived type:
~/Location!Customer/
~/Location/OfType('Customer')
So your query should look like this:
http://localhost:80/odata/Location!Customer?select=Id,Name,Town,BankAccountNumber
or
http://localhost:80/odata/Location/OfType('Customer')?select=Id,Name,Town,BankAccountNumber
/EDIT:
QianLi pointed out, that the above blog entry refers to OData V2. In Odata4 inherited types are accessed in the following syntax:
http://host/service/BaseType/Model.SubType
Reference: http://docs.oasis-open.org/odata/odata/v4.0/os/part2-url-conventions/odata-v4.0-os-part2-url-conventions.html#_Toc372793786
You should add not only the Type name but also the namespace of the type.
For example:
http://services.odata.org/TripPinWebApiService/People('russellwhyte')/Trips(1001)/PlanItems/ODataSamples.WebApiService.Models.Flight?$select=StartsAt
The type Flight inherits from the type PlanItem. And ODataSamples.WebApiService.Models is the namespace.
More detailed information of derived type, you can refer to http://www.odata.org/getting-started/advanced-tutorial/ with some live example if you find the spec too long to read...
How can I serialize the given object into JSON but only include properties with the [DataMember] attribute.
User MyUser = new User();
string MessageJson = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(MyUser);
public class User
{
[DataMember]
public string username { get; set; }
public string password { get; set; }
}
You need to use DataContractJsonSerializer for that.
Note that, I think you'll also need DataContract attribute on the class.
You can use JSON.Net.
If a class has many properties and you only want to serialize a small subset of them then adding JsonIgnore to all the others will be tedious and error prone. The way to tackle this scenario is to add the DataContractAttribute to the class and DataMemberAttributes to the properties to serialize. This is opt-in serialization, only the properties you mark up with be serialized, compared to opt-out serialization using JsonIgnoreAttribute.
[DataContract]
public class Computer
{
// included in JSON
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal SalePrice { get; set; }
// ignored
public string Manufacture { get; set; }
public int StockCount { get; set; }
public decimal WholeSalePrice { get; set; }
public DateTime NextShipmentDate { get; set; }
}
You can place the [ScriptIgnore] attribute on the properties that you do not want to include in your result.
I'm using the official MongoDB C# Driver to communicate with my MongoDB Servers.
This is my complete entity scheme:
public class Person
{
public ObjectId _id { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public String Street { get; set; }
public String City { get; set; }
}
Now, in several cases i just want to get the following return:
public class Person_LocationOview
{
public String LastName { get; set; }
public List<Address_CityOnly> Addresses { get; set; }
}
public class Address_CityOnly
{
public String City { get; set; }
}
The default behavior to load all fields and do the mapping by myself
MongoCursor<Person>
is senseless, because I just want to load the specific fields.
With the help of reflection I generate the fields to load and send:
var fields = new { "LastName", "Addresses.City" };
MongoCollection< BsonDocument >.FindAllAs< Person_LocationOview >( )
.SetFields( fields ).ToList( );
I thought the serializer of MongoDB would be intelligent; but the call returns following error:
System.IO.FileFormatException: Required element 'City' for property 'City' of class Models.Address_CityOnly is missing
Any ideas to this requirement?
I've updated the complete MongoDB infrastructure. Now the code works with all viewModels such as Person_LocationOview. With the full scheme classes, the code still crashes and I do not know why.
Now, all my view classes are implementing an own interface (IPartialEntityView).
In my reflection method to get the field names I'll check this inheritance and only then I will load "Addresses.FieldName(s)". If the property type is no default .NET type or does not inherit IPartialEntityView I will use the complete field "Adresses".
That works great.
Thanks for all.