newtonsoft json serializer for Abstract Classes - c#

I've a concrete class below which inherits from abstract class:
[Serializable]
public class MyConcreteClass : MyAbstractClass
{
public string MyProperty { get; set; }
}
[Serializable]
public abstract class MyAbstractClass { }
NewtonSoft JSON Serializer throws exception below when trying to de/serialize MyconcreteClass class:
Newtonsoft.Json.JsonSerializationException: Could not create an
instance of type MyAbstractClass. Type is an interface or abstract
class and cannot be instantiated. Path ....
Did a bit of googling and found this setting below:
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
If I use above setting i.e. TypeNameHandling.All, the error goes away.
Questions in my mind:
Is this is the correct approach to fix this issue (And not sure what this option is not out of box)
Any performance or negative impacts that I should be aware of with this setting.
Thanks.

1. Is this is the correct approach to fix this issue (And not sure what this option is not out of box)
I think it's correct approach to to de/serialize inheritance class with NewtonSoft JSON. When we de/serialize with setting TypeNameHandling = TypeNameHandling.All, the .NET type name will always be included when serializing. Without the type information, it's hard for converter to decide which class will be de/serialized.
2. Any performance or negative impacts that I should be aware of with this setting.
As remarked in Json.NET Documentation, TypeNameHandling should be used with caution when your application deserializes JSON from an external source and you should create a custom SerializationBinder when deserializing with a value other than TypeNameHandling.None.
You could reference the following links
http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm
https://mallibone.com/post/serialize-object-inheritance-with-json.net

Related

is Newtonsoft TypeNameHandling.all with a basic namespace check safe?

On our API we need to take in json, deserialize it to an interface, set a field, and ship it off. To achieve this, on both ends I'm setting the jsonConvert to use TypeNameHandling.All. The endpoint in question is supposed to be fairly locked down, but there's always a chance of someone gaining access and setting $type to a system class with a dangerous constructor or garbage collection method.
My question is would clarifying the namespace of the type before attempting to deserialize it be sufficiently safe? or would there still be a risk of having something like a sub-object with a dangerous class type in the json? If there is still a risk or an exploit I've missed, what other steps can I do to mitigate the danger?
Our company name is at the start of every namespace we use, so in the code below we just check that the type set in the json starts with our company name. The {} at the start is just so the compiler knows it doesn't need to keep the JObject in memory after the check.
{ //check the type is valid
var securityType = JsonConvert.DeserializeObject<JObject>(request.requestJson);
JToken type;
if (securityType.TryGetValue("$type", out type))
{
if (!type.ToString().ToLower().StartsWith("foo")) { //'foo' is our company name, all our namespaces start with foo
await logError($"Possible security violation, client tried to instantiate {type}", clientId: ClientId);
throw new Exception($"Request type {type} not supported, please use an IFoo");
}
}
else
{
throw new Exception("set a type...");
}
}
IFoo requestObject = JsonConvert.DeserializeObject<IFoo>(request.requestJson, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
});
The risk with TypeNameHandling is that an attacker may trick the receiver into constructing an attack gadget - an instance of a type that when constructed, populated or disposed effects an attack on the receiving system. For an overview see
TypeNameHandling caution in Newtonsoft Json
External json vulnerable because of Json.Net TypeNameHandling auto?
If you are going to protect against such attacks by requiring all deserialized types to be in your own company's .Net namespace, be aware that, when serializing with TypeNameHandling.All, "$type" information will appear throughout the JSON token hierarchy, for all arrays and objects (including for .Net types such as List<T>). As such you must needs apply your "$type" check everywhere type information might occur. The easiest way to do this is with a custom serialization binder such as the following:
public class MySerializationBinder : DefaultSerializationBinder
{
const string MyNamespace = "foo"; //'foo' is our company name, all our namespaces start with foo
public override Type BindToType(string assemblyName, string typeName)
{
if (!typeName.StartsWith(MyNamespace, StringComparison.OrdinalIgnoreCase))
throw new JsonSerializationException($"Request type {typeName} not supported, please use an IFoo");
var type = base.BindToType(assemblyName, typeName);
return type;
}
}
Which can be used as follows:
var settings = new JsonSerializerSettings
{
SerializationBinder = new MySerializationBinder(),
TypeNameHandling = TypeNameHandling.All,
};
This has the added advantage of being more performant than your solution since pre-loading into a JObject is no longer required.
However, having done so, you may encounter the following issues:
Even if the root object is always from your company's namespace, the "$type" properties for nested values may not necessarily be in your companies namespace. Specifically, type information for harmless generic system collections such as List<T> and Dictionary<TKey, Value> as well as arrays will be included. You may need to enhance BindToType() to whitelist such types.
Serializing with TypeNameHandling.Objects or TypeNameHandling.Auto can ameliorate the need to whitelist such harmless system types, as type information for such system types is less likely to get included during serialization as compared to TypeNameHandling.All.
To further simplify the type checking as well as to reduce your attack surface overall, you might consider only allowing type information on the root object. To do that, see json.net - how to add property $type ONLY on root object. SuppressItemTypeNameContractResolver from the accepted answer can be used on the receiving side as well as the sending side, to ignore type information on non-root objects.
Alternatively, you could serialize and deserialize with TypeNameHandling.None globally and wrap your root object in a container marked with [JsonProperty(TypeNameHandling = TypeNameHandling.Auto)] like so:
public class Root<TBase>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Auto)]
public TBase Data { get; set; }
}
Since your root objects all seem to implement some interface IFoo you would serialize and deserialize a Root<IFoo> which would restrict the space of possible attack gadgets to classes implementing IFoo -- a much smaller attack surface.
Demo fiddle here.
When deserializing generics, both the outer generic and the inner generic parameter types may need to be sanitized recursively. For instance, if your namespace contains a Generic<T> then checking that the typeName begins with your company's namespace will not protect against an attack via a Generic<SomeAttackGadget>.
Even if you only allow types from your own namespace, it's hard to say that's enough to be sufficiently safe, because we don't know whether any of the classes in your own namespace might be repurposed as attack gadgets.

Can JSON.NET serialise an object using a "sidecar" approach?

The normal approach to serialisation is to apply attributes to your class to describe how serialisation (or deserialization) is to proceed. For example:
[DataContract]
class MyClass
{
[DataMember]
public string Name { get; set; }
}
Is there a way to perform serialisation using JSON.NET without applying attributes to your class, but instead by providing a "sidecar" object that describes what aspects of the class are to be serialised, in some fashion.
The reason I ask relates to separation of concerns. If you have an API that is meant to be agnostic about how requests get to it, then the natural extension of that is that your API data structures should not be getting embellished with serialisation attributes.
Now of course I could take the "content" of one of my API result objects and copy it into another object having a class that does have appropriate serialisation attributes, but in some cases it would seem more desirable to say "Hey, I want to serialise this object, and the object has no serialisation attributes, so here is a separate data structure to describe what to do."
The other place where this would be handy, of course, is with third-party libraries where you have no opportunity to modify the objects (again, you could make copies of the values, but I'm looking for other ways).
You can use JsonSerializerSettings to specify various serialization options. You can specify whether to serialize or not a particular property, how to serialize a particular type or convert its value, and etc.
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
.....
};
settings.Converters.Add(new StringEnumConverter { CamelCaseText = true });
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.Binder = new SomeSerializationBinder(new DefaultSerializationBinder());
var result = JsonConvert.SerializeObject(yourObject, settings);

when we serialize serializable object using JSON.Net, JSON string is different from DatacontractJSON serializer

I have a class some thing like below
[Serializable]
public class sample
{
private int m_width;
private int m_height;
public int Width
{
get
{
return this.m_width;
}
set
{
this.m_width = value;
}
}
public int Height
{
get
{
return this.m_height;
}
set
{
this.m_height = value;
}
}
}
If I use DataContractJsonSerializer to serialize the object of this class i get the json string as below:
{"m_height":1345,"m_width":1234}
If I use Newtonsoft.Json.dll to serialize this I am getting the out put like below:
{"Width":1234,"Height":1345}
Why DataContractSerializer using backing fields for serialization if class marked as serializable ?
Is there any way I can achieve the same thing using Newtonsoft.Json.dll
We have some objects which are marked as [Serializable] so they can be serialised using traditional methods, but which we need to have cleanly serialised in JSON for use with Web API. Setting IgnoreSerializableAttribute to true will stop Newtonsoft.Json from behaving like Microsoft's serialisers and instead it will just serialise the public properties.
TLDR: Add this to WebApiConfig.cs:
((Newtonsoft.Json.Serialization.DefaultContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver).IgnoreSerializableAttribute = true;
Unless you are always communicating from WCF to WCF, your best bet is probably going to be to use the Newtonsoft serializer. Unfortunately, the MS serializer seems to follow some Microsoft-specific standards that do not match the standards that many web apps expect.
Newtonsoft's serializer seems to be more standard, and even MS uses it for WebAPI, and in the Web API http client (nuget will pull it down for you).
Here's another difference that you will find -- try serializing a DateTime type. You will find that the DataContract serialzer serializes the value in a different format that is not compatible with other JSON (you will notice some slashes in it). My understanding is that that alternate format was used by some of the AJAX WebForm controls, but it's specific to Microsoft Webforms.
Here's a little more info about the dates: http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
Here's another thing you might look at:
The differences between DataContractJsonSerializer and Newtonsoft still stand, but as for why you are getting the weird serialization behavior -- you are mixing your serialization standards.
The [Serializable] attribute pertains to the older .Net serialization. DataContractSerialization is backward compatible, but the behavior might be different.
If you want to do this the datacontract way, mark you class with the
[DataContract] attribute instead, and mark each public member that you want to serialize with the [DataMember] attribute. (or remove all serialization attributes, and it should default to all public properties)
That should explain the difference that are seeing, but I would still recommend that you prefer the Newtonsoft serializer.
DefaultContractResolver class in Newtonsoft.Json.dll i found some code which is setting IgnoreSerializableAttribute property to true.
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE || PORTABLE40)
IgnoreSerializableAttribute = true;
#endif
I am using the DotNet4.0 dll so this property set to true and it is ignoring the Serializable attribute. if i make it false it is giving the same output as DataContractSerializer
DataContract serializer requires you to explicitly mark the class with the [DataContract] attribute and then mark each desired property to be serialized with the [DataMember] attribute.
If you do that you will find that the same json string will get out as using the other class.
The issue you are encountering is due to differences of how the 2 serializers treat the default serialization (without additional information specified):
DataContract serializes all private fields unless otherwise specified
NewtonSoft serializes all public properties unless otherwise specified
On how you can make Newtonsoft serialize the private fields i have no other idea than making a wrapper class with m_Width and m_Height properties that on the setters and geters put the values in the correct target properties of the actual object

Web Api Model Binding and Polymorphic Inheritance

I am asking if anyone knows if it is possible to to pass into a Web Api a concrete class that inherits from a abstract class.
For example:
public abstract class A
{
A();
}
public class B : A
{
}
[POST("api/Request/{a}")]
public class Request(A a)
{
}
At present I have looked around and most solutions seem to say that using TypeNameHandling will work.
JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
However this is not that case. Also my model is being passed from a console app to the webapi. I have read that I may be able to deserialize the json object and after attempting this a few times I decide this was not going to work.
I have looked into creating a customer model binder however, I do not want to make my application more complex that it has to be. At present I inherit from the abstract class with 3 models but may in the future extend this. As you may note adding custom model binders may require multiple binders unless there is a way of making one binder generic for all types of the abstract class.
To expand on this in my console app I have instantiated class b as such and then passed it to the ObjectContent before posting to my webapi
item = B();
//serialize and post to web api
MediaTypeFormatter formatter;
JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
formatter = jsonFormatter;
_content = new ObjectContent<A>(item, formatter);
var response = _client.PostAsync("api/Request", _content).Result;
when the webapi action is called the object is null
If one really wanted to implement what is asked in the question, there is a custom way to do it.
First, create a custom json converter that is inherited from JsonConverter, in it pick a target class and deserialize an instance.
Then, in your WebApiConfig.Register you add your new converter into config.Formatters.JsonFormatter.SerializerSettings.Converters and enjoy this monstrosity in action.
Should you do it? No.
Understanding how to use such API will bring no joy to any new users, documenting this will not be easy, and most importantly - there are no benefits in implementing it this way. If input types are different, then they deserve separate API methods with different URLs. If only few properties are different - make them optional.
Why the example did not work?
The TypeNameHandling is from Json.NET, Web API knows nothing about it, and type information is not part of the JSON spec, so there is no standard way to solve this particular issue.
Please change [POST("api/Request/{a}")] to [POST("api/Request")].
Your parameter comes from body, not from route.
You could use JsonSubTypes converter for this purposes.
It is very useful and simple tool for polymorphic deserialization with usage of Json.NET.
This is possible via the default model binding. check below method.
public abstract class RequestBase
{
public int ID { get; set; }
}
public class MyRequest : RequestBase
{
public string Name { get; set; }
}
[RoutePrefix("api/home")]
public class HomeController : ApiController
{
[HttpPost]
[Route("GetName")]
public IHttpActionResult GetName([FromBody]MyRequest _request)
{
return Ok("Test");
}
}

Using XmlSerializer with private and public const properties

What's the simplest way to get XmlSerializer to also serialize private and "public const" properties of a class or struct? Right not all it will output for me is things that are only public. Making it private or adding const is causing the values to not be serialized.
XmlSerializer only looks at public fields and properties. If you need more control, you can implement IXmlSerializable and serialize whatever you would like. Of course, serializing a constant doesn't make much sense since you can't deserialize to a constant.
Even though it's not possible to serialize private properties, you can serialize properties with an internal setter, like this one :
public string Foo { get; internal set; }
To do that, you need to pre-generate the serialization assembly with sgen.exe, and declare this assembly as friend :
[assembly:InternalsVisibleTo("MyAssembly.XmlSerializers")]
Check out DataContractSerializer, introduced in .NET 3.0. It also uses XML format, and in many ways, it is better than XmlSerializer, including dealing with private data.
See http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/ for a full comparison.
If you only have .NET 2.0, there's the BinarySerializer that can deal with private data, but of course it's a binary format.
It doesn't make sense to consider const members, as they aren't per-instance; but if you just mean non-public instance members: consider DataContractSerializer (.NET 3.0) - this is similar to XmlSerializer, but can serialize non-public properties (although it is "opt in").
See here for more.
One other solution the use of Newtonsoft.Json:
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new { root = result });
var xml = (XmlDocument)Newtonsoft.Json.JsonConvert.DeserializeXmlNode(json);
Sure, this one has unfortunately detour via json.
Here's my solution to putting immutable values in a property that will serialize to XML:
[XmlElement]
public string format { get { return "Acme Order Detail XML v3.4.5"; } set { } }

Categories