After upgrading a code base to use Json.NET 8.0.1, some deserialization stumbles. Using Json.NET 7.0.1 everything works fine. Apparently it is the deserialization of a property of type byte[]
that causes the problem. If I remove the byte[]
property it works fine. I can reproduce the behavior using this simple console application:
internal class Program
{
private static void Main(string[] args)
{
Dictionary<string, Account> accounts;
var jsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
};
using (var streamReader = new StreamReader("accounts.json"))
{
var json = streamReader.ReadToEnd();
accounts = JsonConvert.DeserializeObject<Dictionary<string, Account>>(json, jsonSerializerSettings);
}
foreach (var account in accounts)
{
Debug.WriteLine(account.Value.Name);
}
}
}
internal class Account
{
public string Id { get; set; }
public string Name { get; set; }
public byte[] EncryptedPassword { get; set; }
}
The accounts.json
file looks like this:
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[ConsoleApplication1.Account, ConsoleApplication1]], mscorlib",
"lars.michael": {
"$type": "ConsoleApplication1.Account, ConsoleApplication1",
"EncryptedPassword": {
"$type": "System.Byte[], mscorlib",
"$value": "cGFzc3dvcmQ="
},
"Name": "Lars Michael",
"Id": "lars.michael"
},
"john.doe": {
"$type": "ConsoleApplication1.Account, ConsoleApplication1",
"EncryptedPassword": {
"$type": "System.Byte[], mscorlib",
"$value": "cGFzc3dvcmQ="
},
"Name": "John Doe",
"Id": "john.doe"
}
}
Is this possibly a bug in Json.NET 8.0.1 or can I maybe solve this by tweaking the JsonSerializerSettings
?
If anyone is trying to reproduce this, make sure to synchronize the assembly name in the accounts.json
file with the assembly name of the console application (in this case ConsoleApplication1
).
Update
Fixed in change set 70120ce, which will be included in Json.NET 8.0.2.
Original Answer
Confirmed - this appears to be a regression. Consider the following simple test class:
internal class HasByteArray
{
public byte[] EncryptedPassword { get; set; }
}
Now if I try to round-trip the class with TypeNameHandling.Objects
:
private static void TestSimple()
{
var test = new HasByteArray { EncryptedPassword = Convert.FromBase64String("cGFzc3dvcmQ=") };
try
{
TestRoundTrip(test);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
private static void TestRoundTrip<T>(T item)
{
var jsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
};
TestRoundTrip<T>(item, jsonSerializerSettings);
}
private static void TestRoundTrip<T>(T item, JsonSerializerSettings jsonSerializerSettings)
{
var json = JsonConvert.SerializeObject(item, Formatting.Indented, jsonSerializerSettings);
Debug.WriteLine(json);
var item2 = JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
var json2 = JsonConvert.SerializeObject(item2, Formatting.Indented, jsonSerializerSettings);
Debug.WriteLine(json2);
if (!JToken.DeepEquals(JToken.Parse(json), JToken.Parse(json2)))
throw new InvalidOperationException("Round Trip Failed");
}
I get the following exception:
Newtonsoft.Json.JsonSerializationException: Additional text found in JSON string after finishing deserializing object.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalReader.cs:line 196
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\JsonSerializer.cs:line 823
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\JsonSerializer.cs:line 802
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\JsonConvert.cs:line 863
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) in C:\Development\Releases\Json\Working\Newtonsoft.Json\Working-Signed\Src\Newtonsoft.Json\JsonConvert.cs:line 820
at Question34654184.TestClass.TestRoundTrip[T](T item, JsonSerializerSettings jsonSerializerSettings)
at Question34654184.TestClass.TestRoundTrip[T](T item)
at Question34654184.TestClass.TestSimple()
The exception does not occur in Json 7.0. You should report an issue.
In the meantime, you can use the following converter to work around the problem:
public class ByteArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(byte[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
if (token == null)
return null;
switch (token.Type)
{
case JTokenType.Null:
return null;
case JTokenType.String:
return Convert.FromBase64String((string)token);
case JTokenType.Object:
{
var value = (string)token["$value"];
return value == null ? null : Convert.FromBase64String(value);
}
default:
throw new JsonSerializationException("Unknown byte array format");
}
}
public override bool CanWrite { get { return false; } } // Use the default implementation for serialization, which is not broken.
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
With settings
var jsonSerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
Converters = new [] { new ByteArrayConverter() },
};
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