反序列化base64编码的字节数组在两个JSON.net库版本中返回不同的结果

时间:2022-10-24 14:16:03
  JObject j = JObject.Parse("{'responseArray':'AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA'}");

  byte[] r = j["responseArray"].ToObject<byte[]>(JsonSerializer.Create(new JsonSerializerSettings()));

Expected Output (works fine with JSON.net v4.5.6.14930)

期望输出(适用于JSON.net v4.5.6.14930)

r =  {byte[24]}
        [0]: 0
        [1]: 0
        [2]: 0
        [3]: 0
        [4]: 0
        [5]: 0
        [6]: 0
        [7]: 0
        [8]: 0
        [9]: 0
        [10]: 0
        [11]: 0
        [12]: 0
        [13]: 0
        [14]: 0
        [15]: 0
        [16]: 0
        [17]: 0
        [18]: 0
        [19]: 0
        [20]: 1
        [21]: 0
        [22]: 0
        [23]: 0

Deserializing the above code in JSON.net v6.0.8.18111 gives errorneous result

在JSON.net v6.0.8.18111中反序列化上述代码会得到错误的结果

Incorrect Output (JSON.net v6.0.8.18111)

不正确的输出(JSON.net v6.0.8.18111)

r= {byte[16]}
    [0]: 170
    [1]: 170
    [2]: 170
    [3]: 170
    [4]: 170
    [5]: 170
    [6]: 170
    [7]: 170
    [8]: 170
    [9]: 170
    [10]: 170
    [11]: 170
    [12]: 170
    [13]: 171
    [14]: 170
    [15]: 170

Am I doing something wrong here?

我做错什么了吗?

Note- The snippet in the question is part of a complex piece of code, where data comes in as JSON parameter to a WebMethod, The responsearray is a part of a complex object sent into the webmethod, that is why we use the JSON.net library for decoding.

注意——问题中的代码片段是复杂代码的一部分,其中数据作为WebMethod的JSON参数输入,responsearray是发送到WebMethod的复杂对象的一部分,这就是为什么我们使用JSON.net库进行解码。

2 个解决方案

#1


6  

Update

更新

This is fixed in Json.NET 7.0 Release 1.

这是用Json固定的。净7.0版本1。

Original Answer

原来的答案

This is a Json.NET issue. The problem is that your string AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA just happens to be successfully parsable as a GUID, in JsonReader.ReadAsBytesInternal():

这是一个Json。网络问题。问题是,你的字符串aaaaaaaaaaaaaaaaaaaaaaaaaaaa正好可以在JsonReader.ReadAsBytesInternal()中成功地与GUID相对应。

            if (s.Length == 0)
            {
                data = new byte[0];
            }
            else if (ConvertUtils.TryConvertGuid(s, out g))
            {
                data = g.ToByteArray();
            }
            else
            {
                data = Convert.FromBase64String(s);
            }

At this traceback:

在这个回溯:

Newtonsoft.Json.JsonReader.ReadAsBytesInternal() Line 517   C#
Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes() Line 74 + 0x9 bytes C#
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, Newtonsoft.Json.Serialization.JsonContract contract = {Newtonsoft.Json.Serialization.JsonPrimitiveContract}, bool hasConverter = false) Line 1853 + 0x8 bytes   C#
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}, bool checkAdditionalContent = false) Line 144 + 0x2f bytes   C#
Newtonsoft.Json.JsonSerializer.DeserializeInternal(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}) Line 710 + 0x52 bytes    C#
Newtonsoft.Json.JsonSerializer.Deserialize(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}) Line 689 + 0x11 bytes    C#
Newtonsoft.Json.Linq.JToken.ToObject(System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}, Newtonsoft.Json.JsonSerializer jsonSerializer = {Newtonsoft.Json.JsonSerializer}) Line 1837 + 0x11 bytes    C#
Newtonsoft.Json.Linq.JToken.ToObject(System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}) Line 1811 + 0x3c bytes  C#
Newtonsoft.Json.Linq.JToken.ToObject<byte[]>() Line 1698 + 0x39 bytes   C#

As you can see, this short-circuits the base 64 decoding. Ugly bug, and rather bad luck on your part to get that exact string.

如你所见,这个电路短路了64位译码。丑陋的虫子,而且你的运气也很差。

To block Json.NET's inappropriate GUID recognition you can create your own global JsonConverter for byte arrays. When you do, Json.NET will pass you the raw string without the "helpful" GUID pattern match:

阻止Json。NET不合适的GUID识别,您可以为字节数组创建自己的全局JsonConverter。当您这样做时,Json。NET会将原始字符串传递给您,而没有“有用的”GUID模式匹配:

public class ByteConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(byte[]);
    }

    public override bool CanWrite { get { return false; } } // Use the default implementation for serialization, which is not broken.

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var value = (string)JToken.Load(reader);
        if (value == null)
            return null;
        if (value.Length == 0)
            return new byte[0];
        return Convert.FromBase64String(value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

And then, to set it globally:

然后,把它设为全球

        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> { new ByteConverter() }
        };

This workaround restores the correct deserialization for byte arrays.

这个工作区会为字节数组还原正确的反序列化。

#2


0  

The problem is that JObject has already parsed the AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA as a string (j["responseArray"].Type == JTokenType.String), so the j["responseArray"].ToObject<byte[]> isn't doing a de-base64.

问题是JObject已经将aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaa解析为字符串(j["responseArray"])。Type == = JTokenType.String),因此j["responseArray"]。ToObject 不是去基64。 []>

You have to parse it directly to a byte[], like:

您必须将它直接解析为一个字节[],如:

public class MyObject
{
    public byte[] responseArray { get; set; }
}

MyObject cl = JsonConvert.DeserializeObject<MyObject>("{'responseArray':'AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA'}");

Clearly you can do the Convert.FromBase64String "manually":

显然你可以进行转换。FromBase64String“手动”:

JObject j = JObject.Parse("{'responseArray':'AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA'}");
byte[] r = Convert.FromBase64String((string)j["responseArray"]);

#1


6  

Update

更新

This is fixed in Json.NET 7.0 Release 1.

这是用Json固定的。净7.0版本1。

Original Answer

原来的答案

This is a Json.NET issue. The problem is that your string AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA just happens to be successfully parsable as a GUID, in JsonReader.ReadAsBytesInternal():

这是一个Json。网络问题。问题是,你的字符串aaaaaaaaaaaaaaaaaaaaaaaaaaaa正好可以在JsonReader.ReadAsBytesInternal()中成功地与GUID相对应。

            if (s.Length == 0)
            {
                data = new byte[0];
            }
            else if (ConvertUtils.TryConvertGuid(s, out g))
            {
                data = g.ToByteArray();
            }
            else
            {
                data = Convert.FromBase64String(s);
            }

At this traceback:

在这个回溯:

Newtonsoft.Json.JsonReader.ReadAsBytesInternal() Line 517   C#
Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes() Line 74 + 0x9 bytes C#
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, Newtonsoft.Json.Serialization.JsonContract contract = {Newtonsoft.Json.Serialization.JsonPrimitiveContract}, bool hasConverter = false) Line 1853 + 0x8 bytes   C#
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}, bool checkAdditionalContent = false) Line 144 + 0x2f bytes   C#
Newtonsoft.Json.JsonSerializer.DeserializeInternal(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}) Line 710 + 0x52 bytes    C#
Newtonsoft.Json.JsonSerializer.Deserialize(Newtonsoft.Json.JsonReader reader = {Newtonsoft.Json.Linq.JTokenReader}, System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}) Line 689 + 0x11 bytes    C#
Newtonsoft.Json.Linq.JToken.ToObject(System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}, Newtonsoft.Json.JsonSerializer jsonSerializer = {Newtonsoft.Json.JsonSerializer}) Line 1837 + 0x11 bytes    C#
Newtonsoft.Json.Linq.JToken.ToObject(System.Type objectType = {Name = "Byte[]" FullName = "System.Byte[]"}) Line 1811 + 0x3c bytes  C#
Newtonsoft.Json.Linq.JToken.ToObject<byte[]>() Line 1698 + 0x39 bytes   C#

As you can see, this short-circuits the base 64 decoding. Ugly bug, and rather bad luck on your part to get that exact string.

如你所见,这个电路短路了64位译码。丑陋的虫子,而且你的运气也很差。

To block Json.NET's inappropriate GUID recognition you can create your own global JsonConverter for byte arrays. When you do, Json.NET will pass you the raw string without the "helpful" GUID pattern match:

阻止Json。NET不合适的GUID识别,您可以为字节数组创建自己的全局JsonConverter。当您这样做时,Json。NET会将原始字符串传递给您,而没有“有用的”GUID模式匹配:

public class ByteConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(byte[]);
    }

    public override bool CanWrite { get { return false; } } // Use the default implementation for serialization, which is not broken.

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var value = (string)JToken.Load(reader);
        if (value == null)
            return null;
        if (value.Length == 0)
            return new byte[0];
        return Convert.FromBase64String(value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

And then, to set it globally:

然后,把它设为全球

        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> { new ByteConverter() }
        };

This workaround restores the correct deserialization for byte arrays.

这个工作区会为字节数组还原正确的反序列化。

#2


0  

The problem is that JObject has already parsed the AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA as a string (j["responseArray"].Type == JTokenType.String), so the j["responseArray"].ToObject<byte[]> isn't doing a de-base64.

问题是JObject已经将aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaa解析为字符串(j["responseArray"])。Type == = JTokenType.String),因此j["responseArray"]。ToObject 不是去基64。 []>

You have to parse it directly to a byte[], like:

您必须将它直接解析为一个字节[],如:

public class MyObject
{
    public byte[] responseArray { get; set; }
}

MyObject cl = JsonConvert.DeserializeObject<MyObject>("{'responseArray':'AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA'}");

Clearly you can do the Convert.FromBase64String "manually":

显然你可以进行转换。FromBase64String“手动”:

JObject j = JObject.Parse("{'responseArray':'AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAA'}");
byte[] r = Convert.FromBase64String((string)j["responseArray"]);