首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为某些值类型数组调用JsonConverter中的默认JsonConverter

为某些值类型数组调用JsonConverter中的默认JsonConverter
EN

Stack Overflow用户
提问于 2020-02-17 01:31:33
回答 1查看 1.3K关注 0票数 2

我正试图实现这里所描述的大致的目标:

递归调用JsonSerializer中的JsonConverter

简而言之,要检查正在反序列化的值,要么在我自己的代码中使用它,要么把它交给默认的反序列化器。

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

代码语言:javascript
复制
...
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[]而不是字典中实现同样的目标。如果是字符串,我想将它传递给默认的处理程序。如果一个整数数组,我会处理好自己。

不幸的是,我无法实现

代码语言:javascript
复制
private class DummyByteArray : byte[] { }

因为字节是一个值类型,不是一个可继承的接口。

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-17 03:48:39

首先,顺便提一下,我注意到Json.NET支持将整数数组和Base64字符串反序列化为byte []数组。也就是说,下面的单元测试断言都是只工作

代码语言:javascript
复制
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 这里

尽管如此,https://stackoverflow.com/q/29719509/3744182这个答案https://stackoverflow.com/q/54550629/3744182提供了几个选项,用于递归调用序列化程序以获得“默认”反序列化:

  1. 如果不需要将JSON预加载到JToken层次结构中,则可以让转换器使用线程静态成员禁用自身,然后递归调用serializer.Deserialize()
  2. 如果确实需要将JSON预加载到JToken层次结构中,则可以将该层次结构嵌入到父容器中,并使用容器成员上的虚拟转换器取代和禁用转换器。

使用选项1的示例转换器可能如下所示:

代码语言:javascript
复制
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 这里

然而,在您的情况下,事情更简单。Json.NET将表示为Base64字符串的byte []数组视为原语,因此您可以简单地将其加载到JToken中,并使用JToken显式转换(JToken toByte[])运算符将其转换为byte[]数组,如下所示:

代码语言:javascript
复制
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 这里

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60254867

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档