Make JSON.NET and Serializable attribute work together - asp.net-mvc

I'm using JSON.NET and had some troubles in the past during WEBAPI objects deserialization. After doing some research I've found that the class was marked with [Serializable]. When I removed this the deserialization was just fine.
More detailed information about this can be found here:
Why won't Web API deserialize this but JSON.Net will?
Now it comes to the problem that I use binaryformatter to create a hash value calculated from this object class.
But Binaryformatter requires that the class must be marked as [Serializable].
Could you recommend me any approach to make both things work at the same time?

Found the solution:
First, check that your Newtonsoft.JSON version is greater than 4.5 or just update with NuGET
According to the version notes, both can work together starting from this version using some extra annotations.
http://james.newtonking.com/archive/2012/04/11/json-net-4-5-release-2-serializable-support-and-bug-fixes
"Now if you are serializing types that have the attribute and don’t want the new behaviour, it can either be overridden on a type using the JsonObjectAttribute"
[JsonObject]
[Serializable]
public class Foobar {
Now it is possible to use JSON.NET and, in my case, the binaryformatter with the [Serializable] attribute.

An alternative to specifying JsonObject on each class is to tell web.api to ignore Serialize attributes globally. This can be done by resetting the DefaultContractResolver on the web api JsonFormatter:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
(using NewtonSoft.Json.Serialization where config is the System.Web.Http.HttpConfiguration)
As of NewtonSoft v4.5 the IgnoreSerializableAttribute property on the DefaultContractResolver is set to true but the web api wrapper, around DefaultContractResolver, has this set to false by default.

I was using a POCO with Serializable attribute. In the first case while Posting Request to a WebApi worked by using the following method:
JsonMediaTypeFormatter f = new JsonMediaTypeFormatter()
{
SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = true
}
}
};
var result = client.PostAsJsonAsync<IEnumerable<Company>>("company/savecompanies", companies).Result;
//I have truncated the below class for demo purpose
[Serializable]
public class Company
{
public string CompanyName {get;set;}
}
However, when I tried to read the response from WebApi (Which was posted back as JSON), the object was not properly deserialized. There was not error, but property values were null. The below code did not work:
var readObject = result.Content.ReadAsAsync<IEnumerable<Company>>().Result;
I read the documentation as given on Newtonsoft.Json website https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm and found the following and I quote from that site:
Json.NET attributes take precedence over standard .NET serialization
attributes (e.g. if both JsonPropertyAttribute and DataMemberAttribute
are present on a property and both customize the name, the name from
JsonPropertyAttribute will be used).
So, it was clear if Newtonsoft.Json attributes are present before the standard .NET attributes they will take precedence. Hence I could use the same class for two purposes. One, when I want to post to a WebApi, Newtonsoft Json serializer will kick in and Two, when I want to use BinaryFormatter.Serialize() method std .NET Serializable attribute will work.
The same was confirmed with the answer given above by #Javier.
So I modified the Company Class as under:
[JsonObject]
[Serializable]
public class Company
{
public string CompanyName {get;set;}
}
I was able to use the same class for both purposes. And there was no need for the below code:
JsonMediaTypeFormatter f = new JsonMediaTypeFormatter()
{
SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = true
}
}
};

Related

Customize swagger schema for a nested type using swashbuckle

I have an api (ASP.NET Core 3.0) that allows users to search a document database using various query parameters to filter and order the results. One of the parameters is an Order parameter that defines how to order the results. The acceptable values are limited to the values of an enum.
I now need to add more behavior to the enum, so I re-wrote it as an Enumeration Class so that I can add object-oriented behavior to it. The problem I now have is that Swashbuckle flattens out the properties of the Enumeration rather than leaving it as a single parameter. Here are my enumeration and parameter classes:
// Enumeration
public class DocSearchOrder : Enumeration {
public static readonly DocSearchOrder DocType = new DocSearchOrder(2, nameof(DocType));
public static readonly DocSearchOrder DocTypeDesc = new DocSearchOrder(3, nameof(DocTypeDesc));
public static readonly DocSearchOrder DocDate = new DocSearchOrder(4, nameof(DocDate));
public static readonly DocSearchOrder DocDateDesc = new DocSearchOrder(5, nameof(DocDateDesc));
public DocSearchOrder(int value, string name) : base(value, name) {
}
}
// Search Parameters
public class DocSearchParameters {
public DocSearchOrder? Order { get; set; }
// Lots of other search parameters
}
Then the method that uses it:
public async Task<IActionResult> GetAsync([FromQuery] DocSearchParameters searchParams) {
// Do the search
}
Swashbuckle flattens searchParams.Order into DocSearchOrder.Id and DocSearchOrder.Name.
The behavior I want to achieve is for my Swagger UI to continue to show a dropdown of the available values (DocSearchOrder.Name) that a user can select from with the parameter named "Order". You then pass one of those string values and a custom model binder converts the string to the Enumeration class instance.
I've tried writing a custom IOperationFilter but that only seems to work for modifying the schema of types passed to the GetAsync method, I can't intercept the schema generation for searchParams.Order. I thought what I'd be able to do is somehow intercept the schema generation for any property that is an Enumeration and generate an enum schema for it instead of an object schema, but I don't know how to intercept it.
So my question is: is there a way to customize the schema generation for a nested type? Or is there another way to go about this and I'm doing it all wrong? :)
How about a regular enum:
public enum DocSearchOrder
{
DocType = 2,
DocTypeDesc = 3,
DocDate = 4,
DocDateDesc = 5
}
I think that would that be easier, and there should not give you much trouble
Here is an example from one of mine:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=TestEnum#/TestEnum/TestEnum_Get

Web Api OData Query for custom type

I've defined my own struct that represents DateTime with TimeZoneInfo so I can work with UTC time while keeping the information about timezone.
I would like to get these objects with OData query, but it fails when I try to use $orderby on this properties of this type. I was able to get results when I queried $orderBy=Timestamp/Value/UniversalTime but I would like to use just $orderBy=Timestamp
Is there any possibility to order collection with this type?
public struct DateTimeWithZone : IComparable<DateTime>, IComparable<DateTimeWithZone>, IFormattable
{
private readonly DateTime _utcDateTime;
private readonly TimeZoneInfo _timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
{
if (timeZone == null)
{
throw new NoNullAllowedException(nameof(timeZone));
}
_utcDateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
_timeZone = timeZone;
}
...
}
With model defined like this:
public class ClientViewModel
{
public string Name { get; set; }
public DateTimeWithZone? Timestamp { get; set; }
}
And this is how it is used:
public IHttpActionResult GetAll(ODataQueryOptions<ClientViewModel> options)
{
var fromService = _clientsClient.GetAllClients().MapTo<ClientViewModel>(MappingStrategy).AsQueryable();
var totalCount = fromService.Count();
var results = options.ApplyTo(fromService); // <-- Fails here
return Ok(new PageResult<ClientViewModel>(
results as IEnumerable<ClientViewModel>,
Request.ODataProperties().NextLink,
totalCount));
}
Fails with The $orderby expression must evaluate to a single value of primitive type.
we had some similar issue with complex type ordering. Maybe this can be of assistance in your scenario as well. In our case (which is not 100% identical) we use a two phase approach:
Rewriting ODataQueryOptions
separating the extneral model (ODATA) and the internal model (EntityFramework in our case)
Rewriting ODataQueryOptions
You mention that the format $orderBy=Timestamp/Value/UniversalTime is accepted and is processed properly by ODATA. So you can rewrite the value basically by extracting the $orderby value and reinserting it with in your working format.
I described two ways on how to do this in my post Modifying ODataQueryOptions on the fly (full code included), which take existing options recreate new options by constructing a new Uri. In your case you would extract Timestamp from $orderBy=Timestamp and reinsert as with $orderBy=Timestamp/Value/UniversalTime.
Separating External and Internal Model
In addition, we used two models for the public facing API and the internal / persistence layer. On the internal side we used different properties which we grouped into a navigation property (which only exists on the public side). With this approach the user is able to specify an option via an $expand=VirtualNavigationProperty/TimeZoneInfo and $orderby=.... Internally you do not have to use the complex data type, but keep using DateTimeOffset which already holds that information. I described this separation and mapping of virtual navigation properties in the following post:
Separating your ODATA Models from the Persistence Layer with AutoMapper
More Fun with your ODATA Models and AutoMapper
According to your question it should be sufficient to rewrite the query options in the controller as you did mention that the (little bit longer) $orderby format is already working as expected and you only wanted a more convenient query syntax.
Regards, Ronald

F#, Serialize dynamically generated objects with WebAPI

I am attempting to create Web API controller in F# which returns objects from Entity Framework. SharpObject and SharpContext are my object and DbContext respectively defined in a c# project.
/// Retrieves values.
[<RoutePrefix("api2/values")>]
type ValuesController() =
inherit ApiController()
let values = [| "value1"; "value2" |]
/// Gets all values.
[<Route("")>]
member x.Get() : IEnumerable<SharpObject> =
use context = new SharpContext()
context.SharpObjects.ToList() :> IEnumerable<SharpObject>
Here is SharpObject with the SerializableAttribute.
[Serializable]
public class SharpObject
{
[Key]
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
The error that I am getting is this:
The type System.Data.Entity.DynamicProxies.SharpObject_3A697B5C46C0BF76858FEAFC93BFED36DD8D4CA2CEACBB178D2D3C38BB2D2052 was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
When I de-compile this using ILSpy, it looks like this:
[Route("")]
public IEnumerable<SharpObject> Get()
{
SharpContext context = new SharpContext();
IEnumerable<SharpObject> result;
try
{
result = (IEnumerable<SharpObject>)context.SharpObjects.ToList<SharpObject>();
}
finally
{
IDisposable disposable = context as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
return result;
}
What is the best way to get my list to show through in f#?
This happens because the object that you get from EF is not, in fact, of type SharpObject, but rather of that scarily named type, which inherits from SharpObject. This type is called "proxy" and is dynamically generated by EF in order to provide certain services (such as lazy loading, see below).
Because your action is declared as returning IEnumerable<SharpObject>, the default WebAPI's XML serializer expects to find object of that type, and so rightly complains upon finding an object of different type.
One temporary, bandaid-style fix that you can try is to remove the virtual keywords from your entity (why do you have them there, anyway?). It is the presence of the virtual keywords that causes EF to generate the proxy type. Absent virtual, no proxy will be generated, thus making the XML serializer happy.
This, however, will not work once you extend your model to include navigation properties with lazy loading. Those properties, you must make virtual, otherwise lazy loading won't work.
So the correct fix is not to use the same type for both DB-facing DTO and client-facing DTO. Use different types.
Using the same type for these two purposes may seem "convenient" at first, but this road quickly leads to numerous problems. One of small technical problems you have already discovered. But even absent those, conceptually, you almost never, ever want to just serve up your DB records directly to the untrusted user. Some of possible consequences include security holes, badly factored UI code, badly factored database structure, performance problems, and so on.
Bad idea. Don't do it.
P.S. This doesn't actually have anything to do with F#.

JsonIgnore not working in System.Web.Mvc.Controller

I have a web-API project and a simple class with a few properties, some are marked <JsonIgnore>.
In my MVC-controller I put Return Json(instanceOfMyClass, JsonRequestBehavior.AllowGet). All members are serialized.
I put Return Json(Of MyClass)(instanceOfMyClass) in my WEBAPI-controller. Only the members I intend to serialize are present.
How can I ignore these properties independent of the controller that's going to serialize.
The JsonResult in MVC does not actually use JSON.NET which is why [JsonIgnore] is not working. Instead it uses the JavaScriptSerializer class.
To make the JavaScriptSerializer skip a property, you can you the [ScriptIgnore] attribute on your model property.
An alternative would be to make a custom ActionResult that uses JSON.NET's JsonConvert to serialize the object which would then honor the [JsonIgnore] attribute.
In case it helps anyone, it didn't seem possible or straightforward to use [ScriptIgnore] in my .net Core app, so I did this:
public IActionResult Index()
{
Response.ContentType = "text/json";
return Content(JsonConvert.SerializeObject(instanceOfMyClass));
}

How can I exclude some public properties from being serialized into a JsonResult?

I have a custom viewmodel which serialized using a JsonResult. The ViewModel has some properties which have to be public, but at the same time these properties should not be visible in the resulting Json output.
I've already tried using the [NonSerialized] attribute, but that did not seem to have any effect.
Is there any simple way to do this? Or would I have to code my own result type (in which case I probably won't bother)?
You can put a [ScriptIgnore] attribute on the members that shouldn't be serialized. See ScriptIgnoreAttribute Class in MSDN for an example.
Just create an interface to return instead of a class.
public interface IMyViewModel {
string MyPublicProperty { get; set; }
}
Then create a class that inherits the interface
public class MyViewModel : IMyViewModel {
public string MyPublicProperty { get; set; }
public string MyNotSoPublicProperty { get; set; }
}
And return the interface, not the class, in the Controller Action
public JsonResult MyJson(){
IMyViewModel model = new MyViewModel();
return Json(model);
}
And the resulting JSON will be
{
'MyPublicProperty': ''
}
One of the challenges in client-side scripting is, that if you're changing your classes, you have no idea whether you're destroying the client-side implementation or not. If you use interface types in your JSON, you understand that if you change the interface, you're doing something that potentially may be killing the client side implementation. And it also saves you from double-checking the client side in vain if you're changing something that is NOT in the inteface (thus not being serialized).
Also, many times, your ViewModels might have large collections or complex types in them that you don't necessarily want to output to the client. These might take a long time to serialize or expose information that simply does not belong into the client code. Using interfaces will make it more transparent to know what is being in the output.
Also, using attributes such as [ScriptIgnore] on a property only applies to a specific scenario (JavaScript Serialization) forcing you to face the exact same problem if you're later serializing to XML for example. This would unnecessarily litter your viewmodels with tons of attributes. How many of them you really want in there? Using intefaces applies anywhere and no viewmodel needs to be littered with extra attributes.
Have a look at JSON.NET from James Newton-King. It'll do what you're looking for.
Extend the JavaScriptConverter class to not include properties with the NonSerializedAttribute. Then you can create a custom ActionResult that uses your JavaScriptConverter to serialize the object.
This creates a solid and testable class without having to (re)generate wrapper classes or using anonymous objects.
You can create a wrapper class that exposes only those properties that you want in the JsonResult. In the example below, Cow has 2 properties - "Leg" and "Moo". Suppose you want to only expose "Leg" as a property. Then
Dim cw as CowWrapper = New CowWrapper(c)
will return a wrapper class that only exposes "Leg". This is also useful for things like DataGridView if you only want to display some subset of the properties.
Public Class Cow
Public ReadOnly Property Leg() as String
get
return "leg"
end get
end Property
Public ReadOnly Property Moo() as String
get
return "moo"
end get
end Property
end class
Public Class CowWrapper
Private m_cow as Cow = Nothing
Public Sub New(ByVal cow as Cow)
m_cow = cow
end Sub
m_cow = cow
Public ReadOnly Property Leg() as String
get
return m_cow.Leg()
end get
end Property
end Class
Not exactly the answer you're looking for, but you can cheat Json() using the following code and anonymous classes:
MyModel model = ...;
return Json(new MyModel {model.Prop1, model.Prop2});
I needed the answer to this for ASP.NET Core 6.x and couldn't find it.
I finally found the answer and it is :
[System.Text.Json.Serialization.JsonIgnore]
Here's an example in a class
class Sample{
// Item will not be serialized
[System.Text.Json.Serialization.JsonIgnore]
String Item{get;set;}
// Count will be serialized
int Count{get;set;}
}

Resources