Newtonsoft Json.Net - 如何在反序列化时有条件地添加(或跳过)数组中的项目?
Newtonsoft Json.Net - How to conditionally add (or skip) items in an array when deserializing?
我正在寻找性能优化的解决方案我正在尝试为一些代码准备就绪我已经使用了金融市场订单簿,基本上是我要返回的 JSON 对象有一个数组类型 属性 包含 hundreds/thousands 个订单对象,但是我只对其中的前 10 到 20 个感兴趣(需要动态确定)。从性能的角度来看,我更愿意在添加我需要的项目后跳过反序列化和添加数组中的每个项目。澄清一下,它始终是我实际需要的数组中的前 10 到 20 个项目,之后的所有项目都可以排除。
在Json.NET中有什么方法可以做到这一点吗?我一直在查看 JsonConverters,但无法弄明白。
您可以创建以下 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 []
类型的数组在运行时被截断为某个特定长度。如果这不是真的,并且您需要在运行时单独指定不同数组的不同最大长度,您将需要一个更复杂的解决方案,可能涉及自定义合同解析器。
演示 fiddle here.
我正在寻找性能优化的解决方案我正在尝试为一些代码准备就绪我已经使用了金融市场订单簿,基本上是我要返回的 JSON 对象有一个数组类型 属性 包含 hundreds/thousands 个订单对象,但是我只对其中的前 10 到 20 个感兴趣(需要动态确定)。从性能的角度来看,我更愿意在添加我需要的项目后跳过反序列化和添加数组中的每个项目。澄清一下,它始终是我实际需要的数组中的前 10 到 20 个项目,之后的所有项目都可以排除。
在Json.NET中有什么方法可以做到这一点吗?我一直在查看 JsonConverters,但无法弄明白。
您可以创建以下 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 []
类型的数组在运行时被截断为某个特定长度。如果这不是真的,并且您需要在运行时单独指定不同数组的不同最大长度,您将需要一个更复杂的解决方案,可能涉及自定义合同解析器。
演示 fiddle here.