I need to use Json.net to serialize my object to the client side, which it does brilliantly. However I want to extend it's behaviour to use more attributes during serialization, specifically attributes from the Data Annotations namespace.
Here's one example:
I need to output a date, and rather than send the date to the client side, and format it there, I want to control the format of the output on the server side.
[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime BirthDate { get;set;}
I looked at using a converter to process this, but at the stage a converter is executed, it has no reference back to the member information of the declaring property, so I wouldn't be able to examine the attribute. I'm assuming I would need to override/extend the IContractResolver behaviour, but there is very little documentation on how all the pieces fit, like JsonContract etc.
I just need some pointers as to how the whole thing hangs together, and I'll work the rest out. Thanks in advance to anyone kind enough to assist.
Related
Is it possible to force the way DateTimes are displayed in OData while keeping all functionnalities of IQueryable<> ?
Actually, I created a basic endpoint exposing data from my Database. One of the property is a DateTime and is displayed like that :
"MyDate": "2017-01-07T00:00:00",
However, I'd like to display it with another DateTime format, like dd/MM/yyyy
I tried this promising answer but my property has still the same undesired format when I call my endpoint with Postman.
How to force DateTime format in OData, while preserving OData filtering on this column and let the client decides to query, for example, all data with MyDate>= n days ?
However, I'd like to display it with another DateTime format, like dd/MM/yyyy
Then do not display it directly. The date format in odata is defined in the specs do your code knows what to expect and can then format it for public consumption IF it desires to show it. The whole concept of a standard is that people consuming it know what to expect. In particular - if you write your classes manually to consume them you do something wrong, the idea behind odata is that a code generator generates the client code, and it need to know waht to parse (into a javascript odata class).
Odata is NOT supposed to necessariyl be presented directly to the user. The date format is part of the standard. If you change it, it is not odata.
Oh, and another note: The datetime foramt you see there is actually a standard for which Javascript has methods and libraries to manipulate them. WHich is another reason you WANT this. Your custom format is something you have to write all the libraries for yourself.
I am adding my solution here as this was my first hit on Google.
I ran into the same issue, and tried the same solution with no success.
Note that this answer is based on an Asp.NET Core Web Api.
I based my answer on WebAPI OData Datetime serialization
but the way they added the Custom payload value converter doesn't seem applicable anymore.
You have to create a class derived from ODataPayloadValueConverter on which you override the ConvertToPayloadValue function:
namespace [YOUR_NAMESPACE_HERE];
using Microsoft.OData;
using Microsoft.OData.Edm;
public class CustomODataPayloadConverter : ODataPayloadValueConverter
{
public override object ConvertToPayloadValue(object value, IEdmTypeReference edmTypeReference)
{
if (edmTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.DateTimeOffset)
{
var dateTimeOffset = (DateTimeOffset)value;
return dateTimeOffset.ToString("dd/MM/yyyy");
}
return base.ConvertToPayloadValue(value, edmTypeReference);
}
}
You add the payload value converter in the Action<IServiceCollection> which is the 3rd parameter in ODataOptions.AddRouteComponents.
builder.AddOData(options =>
{
options.AddRouteComponents("api/v1", modelBuilder.GetEdmModel(), actions =>
{
actions.AddSingleton(typeof(ODataPayloadValueConverter), new CustomODataPayloadConverter());
});
});
I am accessing the ODataOptions inside IMVCBuilder.AddOData which in turn is an extension method.
using Microsoft.AspNetCore.OData;
Which is found in the 'Microsoft.AspNetCore.OData.Mvc' NuGet package.
My Postman response:
{
"#odata.context": "http://localhost:5267/api/v1/$metadata#ENTITY_NAME",
"value": [
{
"Id": "98aa1283-f17f-4d12-8e51-59a4ead2a23e",
"myDateTime": "28/09/2022"
}
]
}
where myDateTime is ofcourse, a DateTime.
Someone send JSON to me. It has same format each time. It can be deserialised into dynamic object with var dyn = JsonConvert.DeserializeObject<dynamic>(rawJson);. Documentation is bad for my current version or JSON provider while I want to have code-hilighting on all the fields I get.
How to get C# class code from dynamic object so that I could then deserialise into that generated type instead of dynamic object with var oldschool = JsonConvert.DeserializeObject<GeneratedTypeFromDynamicObject>(rawJson);?
Generating a type at run-time isn't going to help you much because you need the type at compile time in order to get early binding, type safety, and Intellisense. A much more practical idea would be to do the following:
Get a representative sample of a JSON string from your logs.
Paste the JSON into a class and save it as c# code in your project.
Start using the class as the type argument to your deserialization call.
After you do this, you will have the correct class in your code base and you can compile against it. If the folks that send you the JSON decide to change the message, any new fields will be ignored. If they start removing things though then you have problems, just as if they had changed the WSDL for a SOAP service. You will have to repeat the above steps, and correct any breaking changes in your code. The nice thing is that you will have the breaking changes to guide you at compile time :)
I am using json.net library to serialize an object which has a decimal value that represents a cost. When serializing, I want the json to look something like '$400,000' instead of '400000.0'.
Is there a way that I can accomplish this in an easy and efficient way? This object contains many other secondary objects and subsequent properties.
The application will be used locally and the specs say that the output should be in human readable format. Culture variance isn't of any importance in this context.
There's no easy way to perform this since you can't work around a fact that serializer is directly accessing your properties.
If you need a formatted JSON output I would recommend writing a helper class that wraps the class you want to expose. Unfortunately I've done this once for some XML report and really the easiest way went something like this:
class PersonXml
{
Person _person;
void PersonXml(Person person) { _person = person; }
string Age { get { return _person.Age + " years"; } }
}
This is just a simple example I wrote on the fly but the principle is always the same. Even if some advanced JSON serializer offered me ways to format my output, I'd really keep this concept out of my main objects in a separate file with separate helper classes.
Again this isn't quite the solution, in my opinion it should never come to this but if it does, this is the lesser of the evils in my experience.
Also, just note that if you reference the class in your property getters there's a danger of null reference.
As D Stanley suggested, I changed the field type to string from decimal as data in it was only being read and not used for data manipulation.
I want to make a Configuration Data Manager. This would allow multiple services to store and access configuration data that is common to all of them.
For the purposes of the Manager, I've decided to create a configuration class object - basically what every configuration data entry would look like:
Name, type, and value.
In the object these would all be strings that discribe the configuration data object itself. Once it has gotten this data from its database as strings, it would put it into this configuration object.
Then, I want it to send it through WCF to its destination. BUT, I don't want to send a serialized version of the configuration object, but rather a serialized version of the object discribed by the configuration object.
The reason I'd like to do this is so that
The Data Manager does not need to know anything about the configuration data.
So I can add configuration objects easily without changing the service. Of course, I should be able to do all of the CRUD operations, not just read.
Summary:
Input: string of name, type and value
Output: Serialized output of the object; the object itself is "type name = value"
Questions:
Is this a good method for storing and accessing the data?
How can I/can I serialize in this manner?
What would the function prototype of a getConfigurationData method look like?
I have decided to go in a different direction, thanks for the help.
Is this a good method for storing and accessing the data?
That is difficult to answer, the best I can give you is both a "yes" and a "No". Yes, It's not a bad idea to isolate the serialization/rehydration of this data.... and No, I don't really care much for the way you describe doing it. I'm not sure I would want it stored in text unless I plan on editing it by hand, and if I'm editing it by hand, I'm not sure I'd want it in a database. It could be done; just not sure you're really on the right track yet.
How can I/can I serialize in this manner?
Don't build your own, never that. Use a well-known format that already exists. Either XML or JSON will serve for hand-editable, or there are several binary formats (BSON, protobuffers) if you do not need to be able to edit it.
What would the function prototype of a getConfigurationData method look like?
I would first break-down the 'general' aka common configuration into a seperate call from the service specific configuration. This enables getConfigurationData to simply return a rich type for common information. Then either add a extra param and property for service specific data, or add another method. As an example:
[DataContract]
public class ConfigurationInfo
{
[DataMember]
public string Foo;
...
// This string is a json/xml blob specific to the 'svcType' parameter
[DataMember]
public string ServiceConfig;
}
[DataContract]
public interface IServiceHost
{
ConfigurationInfo GetConfigurationData(string svcType);
}
Obviously you place a little burden on the caller to parse the 'ServiceConfig'; however, your server can treat it as an opaque string value. It's only job is to associate it with the appropriate svcType and store/fetch the correct value.
Quick question regarding Automapper.
I'm mapping data from an incoming web service, where datetimes fields are not null, but sometimes have the value of DateTime.MinValue as a work-around (I don't own this service so I'm at their mercy I'm afraid). I'm mapping from those objects to Linq To Sql objects created off of a db model I created based on the service description.
Everything is working fine, until I try to submit new rows where DateTime.MinValue is set on the datetime fields. Of course, the solution is to replace DateTime.MinValue with SqlDateTime.MinValue.Value as Sql Server chokes on DateTime.MinValue.
I would like to do this automatically with the mapping... that is, replace this on any datetime fields in the incoming web service call without having to specify on a field by field basis.
Formatters won't do the trick since they only deal with string formatting, and IValueResolver doesn't appear to do what I need either. That appears to be called once per instance of the parent type, not per field within the type (I tried to use it with .ForAllMembers()). Perhaps I did something wrong.
If the NullSubstitute method was generic -- that is, if the signature was something like Substitute<T, Func<T,T>>, then I think I'd be in business.
Documentation / online samples haven't provided much in the way of direction... and while I could start digging through the source, I was hoping someone with more in-depth knowledge of the system could comment (Jimmy?).
I would assume that something like this should be a feature somewhere, but then again, there may be other issues at play which complicate implementing something like this.
Thanks in advance for any assistance.
Would TypeConverter help?