【问题标题】:Newtonsoft Json.Net - How to conditionally add (or skip) items in an array when deserializing?Newtonsoft Json.Net - 反序列化时如何有条件地添加(或跳过)数组中的项目?
【发布时间】:2021-11-30 02:22:15
【问题描述】:

我正在寻找一种性能优化的解决方案我正在尝试为一些我使用金融市场订单簿的代码准备好,基本上我要返回的 JSON 对象具有一个数组类型属性,其中包含数百/数千个订单对象,但我只对其中的前 10 到 20 个感兴趣(需要动态确定)。从性能的角度来看,在我添加了我需要的项之后,我更愿意跳过反序列化和添加数组中的每个项。为了澄清它始终是我实际需要的数组中的前 10 到 20 个项目,之后的所有内容都可以排除。

有没有办法在 Json.NET 中实现这一点?我一直在看 JsonConverters 但无法弄清楚。

【问题讨论】:

  • 我可以假设您为此使用c# 吗?

标签: json json.net deserialization json-deserialization


【解决方案1】:

您可以创建以下JsonConverter<T []>,它将跳过超出特定计数的数组条目:

public class MaxLengthArrayConverter<T> : JsonConverter<T []>
{
    public MaxLengthArrayConverter(int maxLength) => this.MaxLength = maxLength >= 0 ? maxLength : throw new ArgumentException(nameof(maxLength));

    public int MaxLength { get; }

    public override T [] ReadJson(JsonReader reader, Type objectType, T [] existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return null;
        reader.AssertTokenType(JsonToken.StartArray);
        var list = new List<T>();
        while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
        {
            if (list.Count < MaxLength)
                list.Add(serializer.Deserialize<T>(reader));
            else
                reader.Skip();
        }
        return list.ToArray();
    }

    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, T [] value, JsonSerializer serializer) => throw new NotImplementedException();
}

public static partial class JsonExtensions
{
    public static JsonReader AssertTokenType(this JsonReader reader, JsonToken tokenType) => 
        reader.TokenType == tokenType ? reader : throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, tokenType));
    
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    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;
    }
}

然后,假设您的金融市场订单簿模型如下所示:

public class OrderBook
{
    public Order [] Orders { get; set; }
}

可以如下反序列化:

int maxLength = 20;  // Or whatever you want.
var settings = new JsonSerializerSettings
{
    Converters = { new MaxLengthArrayConverter<Order>(maxLength) },
};
var model = JsonConvert.DeserializeObject<OrderBook>(json, settings);

Assert.IsTrue(model.Orders.Length <= maxLength);

注意事项:

  • 在您的问题中,您只提到了数组,但如果您的模型实际上使用的是列表而不是数组,请改用以下转换器:

    public class MaxLengthListConverter<T> : JsonConverter<List<T>>
    {
        public MaxLengthListConverter(int maxLength) => this.MaxLength = maxLength >= 0 ? maxLength : throw new ArgumentException(nameof(maxLength));
    
        public int MaxLength { get; }
    
        public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
                return null;
            reader.AssertTokenType(JsonToken.StartArray);
            existingValue ??= (List<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
            existingValue.Clear();
            while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
            {
                if (existingValue.Count < MaxLength)
                    existingValue.Add(serializer.Deserialize<T>(reader));
                else
                    reader.Skip();
            }
            return existingValue;
        }
    
        public override bool CanWrite => false;
        public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) => throw new NotImplementedException();
    }
    
  • 此答案假定您希望模型中的所有 T [] 类型的数组在运行时被截断为某个特定长度。如果这不是真的,并且您需要在运行时单独指定不同数组的不同最大长度,您将需要一个更复杂的解决方案,可能涉及自定义合约解析器。

演示小提琴here.

【讨论】:

  • 看起来不错,非常感谢!
猜你喜欢
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 2018-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多