这是我的情况:我正在尝试使用 Newtonsoft.Json 13.0.3 通过使用已实例化的数据进行反序列化。
我知道这个存在:JsonConvert.PopulateObject,但这并不能解决问题,对我来说是这样。
// MainData, a class we will let Json.net instanciate
public class MainData
{
public List<SmallerData> _smallerDataArray = new();
}
// SmallerData, a class we do not want Json.net to instanciate
public class SmallerData
{
}
...
public static MainData DeserializeFromPool(string jsonString)
{
List<SmallerData> pooledData = new();
pooledData.Add(new SmallerData());
pooledData.Add(new SmallerData())
pooledData.Add(new SmallerData())
return JsonConvert.DeserializeObject<MainData>(jsonString);
}
不知何故,我希望 JsonConvert.DeserializeObject 在实例化 SmallerData 时从 pooledData 中提取,而不是创建新的。
我一直在研究转换器合同什么的,但找不到方法。以前有人尝试过吗?
如果您好奇我为什么要这样做:这是我为减少 json 使用过程中垃圾生成所做的众多努力之一。
CustomCreationConverter<SmallerData>
从内存池中提取对象。
假设您的
SmallerData
看起来像这样,所有线程都有一个共享池:
public class SmallerData
{
public static ObjectPool<SmallerData> Pool { get; } = new();
public static SmallerData Create() => Pool.TryRemove(out var o) ? o : new();
}
public class ObjectPool<TObject>
{
readonly ConcurrentQueue<TObject> queue = new(); // I chose a queue to get FIFO behavior but if you want LIFO use ConcurrentStack<T>
public void Add(TObject obj) => queue.Enqueue(obj ?? throw new ArgumentNullException(nameof(obj)));
public void AddRange(params TObject [] objs) => Array.ForEach(objs, o => Add(o));
public bool TryRemove( [System.Diagnostics.CodeAnalysis.NotNullWhen(returnValue: true)] out TObject? obj) => queue.TryDequeue(out obj) && obj is not null;
}
然后你可以定义一个转换器,如下所示:
public class SmallerDataConverter : CustomCreationConverter<SmallerData>
{
public override SmallerData Create(Type objectType) => SmallerData.Create();
}
并通过将转换器添加到settings来反序列化:
SmallerData [] itemsToPool = [new(), new(), new()];
SmallerData.Pool.AddRange(itemsToPool);
var settings = new JsonSerializerSettings
{
Converters = { new SmallerDataConverter() },
};
var root = JsonConvert.DeserializeObject<MainData>(jsonString, settings);
或者使用
attributes将转换器直接应用于
SmallerData
,以确保您的数据类在反序列化时使用池构建:
[JsonConverter(typeof(SmallerDataConverter))]
public class SmallerData
{
public static ObjectPool<SmallerData> Pool { get; } = new();
public static SmallerData Create() => Pool.TryRemove(out var o) ? o : new();
}
无论哪种方式,在反序列化期间都将使用池化实例。 演示小提琴在这里:https://dotnetfiddle.net/CX0OTc
或者,您可以消除全局池并为每个转换器创建一个本地池,如下所示:
public class SmallerDataConverter : CustomCreationConverter<SmallerData>
{
public ConcurrentQueue<SmallerData> Queue { get; } = new(); // I chose a queue to get FIFO behavior but if you want LIFO use ConcurrentStack<T>
public SmallerDataConverter(params SmallerData [] objs) => Array.ForEach(objs, o => Queue.Enqueue(o ?? throw new ArgumentNullException(nameof(o))));
public override SmallerData Create(Type objectType) => Queue.TryDequeue(out var o) && o is not null ? o : new();
}
但是,使用这种方法,您将无法通过属性应用转换器,并且需要将要池化的项目传递到转换器构造函数中,如下所示:
SmallerData [] itemsToPool = [new(), new(), new()];
var settings = new JsonSerializerSettings
{
Converters = { new SmallerDataConverter(itemsToPool) },
};
var root = JsonConvert.DeserializeObject<MainData>(jsonString, settings);
即使池对于给定转换器来说是本地的,最好将其设为线程保存,因为许多应用程序跨线程共享设置。
演示小提琴 #2 这里。