【问题标题】:Call default JsonSerializer in a JsonConverter for certain value type arrays在 JsonConverter 中为某些值类型数组调用默认的 JsonSerializer
【发布时间】:2020-02-17 01:31:33
【问题描述】:

我正在尝试大致实现这里描述的内容:

Recursively call JsonSerializer in a JsonConverter

简而言之;要检查正在反序列化的值,请在我自己的代码中使用它,或者将其交给默认的反序列化器。

该示例使用了一个巧妙的技巧来避免递归调用相同的自定义代码:

...
else if (reader.TokenType == JsonToken.StartObject)
    // Use DummyDictionary to fool JsonSerializer into not using this converter recursively
    dictionary = serializer.Deserialize<DummyDictionary>(reader);
else
    dictionary = new Dictionary<TKey, TValue>();
return dictionary;

/// <summary>
/// Dummy to fool JsonSerializer into not using this converter recursively
/// </summary>
private class DummyDictionary : Dictionary<TKey, TValue> { }

DummyDictionary 类在 Json.Net 寻找新的反序列化器时充当控制流的代理。

我需要为byte[] 而不是字典实现相同的目标。如果它是一个字符串,我想将它传递给默认处理程序。如果是 int 数组,我会自己处理。

很遗憾,我无法实现一个

private class DummyByteArray : byte[] { }

因为 byte 是一种值类型,不是可继承的接口。

如何在不将对象中 byte[] 的每个实例更改为 SomeNoddyByteProxy 的情况下实现所需的控制?

【问题讨论】:

  • json 通常将 byte[] 写入为 base64 编码的字符串。所以只要把它当作一个字符串来读,然后用它做任何你需要的事情吗?我显然错过了一些东西。你能分享更多你正在尝试做的事情吗?可能是您当前转换器的版本?
  • 如果你把它写成一个数字数组?即[1,2,3,4] 然后读取为int 数组并向下转换?或作为 List (作为 dbc 的答案)。或者写一个“while;读一个字节;IEnumberable 方法;和 ToArray() 那个。所有假设你把它写成一个 num 的列表。这就是你正在尝试的吗?

标签: c# serialization json.net


【解决方案1】:

首先,顺便说一句,我注意到 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 answerNewtonsoft Json.NET JsonConverter attribute preserve references issue when deserializing 中给出了一些选项,用于递归调用序列化程序以获取“默认”反序列化:

  1. 如果您不需要将 JSON 预加载到 JToken 层次结构中,您可以使用线程静态成员让转换器禁用自身,然后递归调用 serializer.Deserialize()

  2. 如果您确实需要将 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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-11
    • 2022-01-17
    相关资源
    最近更新 更多