无法使用Json.NET 8.0.1反序列化具有字节数组属性的对象

时间:2016-01-07 11:37:17

标签: c# serialization json.net

升级代码库以使用Json.NET 8.0.1后,一些反序列化会失败。使用Json.NET 7.0.1一切正常。显然,类型byte[]的属性的反序列化会导致问题。如果我删除byte[]属性,它可以正常工作。我可以使用这个简单的控制台应用程序重现行为:

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; }
}

accounts.json文件如下所示:

{
    "$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"
    }
}

这可能是Json.NET 8.0.1中的一个错误,或者我可以通过调整JsonSerializerSettings来解决这个问题吗?

如果有人试图重现这一点,请确保将accounts.json文件中的程序集名称与控制台应用程序的程序集名称同步(在本例中为ConsoleApplication1)。

1 个答案:

答案 0 :(得分:2)

<强>更新

更改集Fixed中的

70120ce,它将包含在Json.NET 8.0.2中。

原始答案

确认 - 这似乎是一种回归。考虑以下简单的测试类:

internal class HasByteArray
{
    public byte[] EncryptedPassword { get; set; }
}

现在,如果我尝试使用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");
    }

我得到以下异常:

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()

Json 7.0中不会发生异常。你应该report an issue

与此同时,您可以使用以下转换器来解决此问题:

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();
    }
}

使用设置

            var jsonSerializerSettings = new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Objects,
                TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
                Converters = new [] { new ByteArrayConverter() },
            };