首先,顺便说一句,我注意到 Json.NET 原生支持将整数数组和 Base64 字符串反序列化为 byte [] 数组。 IE。以下单元测试断言都正常工作:
Assert.IsTrue(JsonConvert.DeserializeObject<byte []>("[1, 2]")
.SequenceEqual(new [] { (byte)1, (byte)2 }));
Assert.IsTrue(JsonConvert.DeserializeObject<byte []>("\"AQI=\"")
.SequenceEqual(new [] { (byte)1, (byte)2 }));
演示小提琴 #1 here.
话虽如此,JSON.Net throws StackOverflowException when using [JsonConvert()] 和 this answer 到 Newtonsoft Json.NET JsonConverter attribute preserve references issue when deserializing 中给出了一些选项,用于递归调用序列化程序以获取“默认”反序列化:
如果您不需要将 JSON 预加载到 JToken 层次结构中,您可以使用线程静态成员让转换器禁用自身,然后递归调用 serializer.Deserialize()。
如果您确实需要将 JSON 预加载到 JToken 层次结构中,您可以将层次结构嵌入到父容器中,并使用容器成员上的虚拟转换器取代和禁用转换器。
使用选项 #1 的示例转换器可能如下所示:
public sealed class ByteConverter : JsonConverter<byte[]>
{
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanRead { get { return !Disabled; } }
public override byte[] ReadJson(JsonReader reader, Type objectType, byte[] existingValue, bool hasExistingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType) // Skip past comments
{
case JsonToken.Null:
return null;
case JsonToken.StartArray:
// Your custom logic here, e.g.:
return serializer.Deserialize<List<byte>>(reader).ToArray();
default:
using (new PushValue<bool>(true, () => Disabled, val => Disabled = val))
return serializer.Deserialize<byte []>(reader);
}
}
// Remainder omitted
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, byte[] value, JsonSerializer serializer) => throw new NotImplementedException();
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
演示小提琴#2 here.
但是,在您的情况下,事情会更简单。 Json.NET 将表示为 Base64 字符串的 byte [] 数组视为基元,因此您可以简单地将其加载到 JToken 并使用 JToken Explicit Conversion (JToken toByte[]) 运算符将其转换为 byte[] 数组,如下所示:
public class ByteConverter : JsonConverter<byte[]>
{
public override byte[] ReadJson(JsonReader reader, Type objectType, byte[] existingValue, bool hasExistingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType) // Skip past comments
{
case JsonToken.Null:
return null;
case JsonToken.StartArray:
// Your custom logic here, e.g.:
return serializer.Deserialize<List<byte>>(reader).ToArray();
default:
return (byte[])JToken.Load(reader);
}
}
// Remainder omitted
这完全避免了使用序列化程序。演示小提琴 #3 here.