According to Json.Net documentation all IEnumerable
types should be serialized as json array.
So I expect the following class:
public class MyClass
{
public IEnumerable<string> Values { get; set; }
}
to be serialized as:
{
"Values": []
}
The problem is that when I use TypeNameHandling=Auto
I get:
{
"Values": {
"$type": "System.String[], mscorlib",
"$values": []
}
}
I need TypeNameHandling=Auto
for other properties but I expect IEnumerable
to use the default serialization. Other types (IList
for example) works as expected.
It is a bug or I missing something?
Here the full code to reproduce the problem:
[Test]
public void Newtonsoft_serialize_list_and_enumerable()
{
var target = new Newtonsoft.Json.JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto
};
var myEvent = new MyClass
{
Values = new string[0]
};
var builder = new StringWriter();
target.Serialize(builder, myEvent);
var json = JObject.Parse(builder.ToString());
Assert.AreEqual(JTokenType.Array, json["Values"].Type);
}
public class MyClass
{
public IEnumerable<string> Values { get; set; }
}
I'm using Newtonsoft 7.0.1.
UPDATE: Here another test using more types:
[Test]
public void Newtonsoft_serialize_list_and_enumerable()
{
var target = new Newtonsoft.Json.JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto
};
var myEvent = new MyClass
{
Values1 = new string[0],
Values2 = new List<string>(),
Values3 = new string[0],
Values4 = new List<string>(),
Values5 = new string[0]
};
var builder = new StringWriter();
target.Serialize(builder, myEvent);
var json = builder.ToString();
}
public class MyClass
{
public IEnumerable<string> Values1 { get; set; }
public IEnumerable<string> Values2 { get; set; }
public IList<string> Values3 { get; set; }
public IList<string> Values4 { get; set; }
public string[] Values5 { get; set; }
}
And this is the results:
{
"Values1": {
"$type": "System.String[], mscorlib",
"$values": []
},
"Values2": [],
"Values3": {
"$type": "System.String[], mscorlib",
"$values": []
},
"Values4": [],
"Values5": []
}
Again I don't understand why I get different results depending on the combination.
The default automatic behaviour of Json.Net, when deserializing into an IEnumerable
or IList
field, is to create a List
instance. If you assign an Array
instance, then the only way to restore your object to it original instance state is for Json.Net to add the $type
meta data, which is what you are seeing. i.e. there are many ways to deserialize into an IEnumerable
field.
By using a List<string>
instance:
var myEvent = new MyClass
{
Values = new List<string>(),
};
with:
public class MyClass
{
public IEnumerable<string> Values { get; set; }
}
results in (when serialized):
{"Values":["hello"]}
Also, if you use an explicit constructable type or use the default List, then Json.Net will use that and omit $type
;
For instance:
string[] Values { get; set; } = new string[0]; // not needed
IEnumerable<string> Values { get; set; } = new string[0]; //$type needed
IEnumerable<string> Values { get; set; } = new Stack<string>(); //$type needed
IEnumerable<string> Values { get; set; } = new List<string>; // not needed
List<string> Values { get; set; } = new List<string>; // not needed
ObservableCollection<string> Values { get; set; } =
new ObservableCollection<string>(); // not needed
This is not a bug but this is expected behavior. You are setting the TypeNameHandling
globally. Instead of globally setting the type name handling you can mark properties with attribute like below to avoid them being included for setting TypeNameHandling.Auto
:
[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
Here's a sample:
public class MyClass
{
// Indicate the property not to apply any type handling
[JsonProperty(TypeNameHandling=TypeNameHandling.None)]
public IList<string> Values { get; set; }
public string Name { get; set; }
}
This will give you desired result without getting affected by any TypeNameHandling
setting declared globally.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With