使用现有子对象从 Json 反序列化

问题描述 投票:0回答:1

这是我的情况:我正在尝试使用 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 使用过程中垃圾生成所做的众多努力之一。

c# json json.net deserialization
1个回答
0
投票

您可以使用

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 这里

© www.soinside.com 2019 - 2024. All rights reserved.