使用json.net 6.0.3 </byte>反序列化IEnumerable <byte>属性时出错

时间:2014-07-30 16:34:44

标签: json.net

鉴于以下课程:

[DataContract]
public class Enumerables
{
    [DataMember]
    public IEnumerable<Byte> ByteMember { get; set; }
}

一个实例初始化为:

var bytes = new byte[] { ... };
var o = new Enumerables { ByteMember = bytes };

序列化产生了这个:

{"ByteMember": "<<base-64-encoded-string>>"}

但是这个字符串不能被反序列化。产生的错误是:

Newtonsoft.Json.JsonSerializationException : Error converting value
"vbMBTToz9gyZj6gZuA59rE7ryu3fCfimjVMn8R6A0277Xs9u" to
type 'System.Collections.Generic.IEnumerable`1[System.Byte]'.
Path 'ByteMember', line 1, position 8084.
----> System.ArgumentException : Could not cast or convert from
System.String to System.Collections.Generic.IEnumerable`1[System.Byte].

我没有看到byte[]List<byte>Collection<byte>属性发生这种情况,这些属性与base-64字符串正确序列化。对于IEnumerable<T>不是字节的任何T,我都不会发现这种情况发生 - 例如,IEnumerable<int>类型的属性反序列化为List<double>,一个有效的实施。

1 个答案:

答案 0 :(得分:1)

IEnumerable<byte>如何序列化取决于分配给它的具体类型。如果具体类型是byte[],那么它将被特别序列化为base-64编码的字符串,而如果它是一些其他具体类型,如List<byte>,它将被序列化为正常数组数字。 ICollection<byte>IList<byte>也是如此。 (DEMO

在反序列化时,Json.Net查看目标类的成员属性的类型,以确定要创建的对象类型。当成员属性是具体类型时,没问题;它创建该类型的实例并尝试从JSON填充它。如果成员类型是一个接口,那么Json.Net必须进行猜测或抛出错误。你可以说Json.Net应该足够聪明,如果成员变量是IEnumerable<byte>并且JSON值是base-64编码的字符串,它应该将字符串转换为byte[]。但这不是它的实施方式。实际上,仅当成员属性为byte[]时才会触发对base-64编码字节数组的特殊处理。由于IEnumerable<byte>没有特殊处理,因此无法将string直接分配给IEnumerable<byte>,从而导致错误。同样,ICollection<byte>IList<byte>也是如此。 (DEMO

如果您希望它与IEnumerable<byte>实现byte[]的类型相同,那么您可以像这样制作自定义JsonConveter

public class EnumerableByteConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(IEnumerable<byte>).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        byte[] bytes = ((IEnumerable<byte>)value).ToArray();
        writer.WriteValue(Convert.ToBase64String(bytes));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        byte[] bytes = Convert.FromBase64String(reader.Value.ToString());
        if (objectType == typeof(byte[]))
        {
            return bytes;
        }
        return new List<byte>(bytes);
    }
}

要使用转换器,请创建JsonSerializerSettings的实例,并将转换器的实例添加到Converters集合中。然后,将设置传递给SerializerObject()DeserializeObject()方法。例如:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new EnumerableByteConverter());

string json = JsonConvert.SerializeObject(obj, settings);

这是working round-trip demo。但请注意,此转换器不会(也不能)处理可能存在的每个可能的IEnumerable<byte>。例如,它不适用于当前实施的ISet<byte>。如果您需要支持此类或其他类型,则需要扩展ReadJson方法来处理该问题。我把它留给你。