【问题标题】:An efficient way to deserialize the same json stream twice by NewtonsoftNewtonsoft 两次反序列化相同 json 流的有效方法
【发布时间】:2020-03-20 23:48:51
【问题描述】:

我有一个在 System.IO.StreamReader 中收到的 json。其结构如下:

{
    "data": [
        {
            "Property1": "Value1",
            "Property2": "Value2"
        },
        {
            "Property1": "Value3",
            "Property2": "Value4"
        }
    ],
    "metaData": {
        (some other structure)
    }
}

我有两个类,一个以Property1 作为数据字段,另一个以Property2 作为数据字段。

我想使用传入流将此 json 反序列化为我拥有的两个类的两个单独集合。

到目前为止,我只能通过首先对流进行字符串化然后调用DeserliazeObject(string) 两次来做到这一点。但是随后不必要地分配了一大块内存。

如何通过对DeserializeObject(streamReader) 的一次调用来做到这一点? Newtonsoft 是否公开任何可以帮助我解决此问题的序列化设置?

这是我的代码,效率低下,因为我在调用反序列化器之前将流转换为字符串:

var responseContent = await response.Content.ReadAsStringAsync();
var jObject = JObject.Parse(responseContent);
var records = GetRecords(jObject);

private IList<(IClass1, IClass2)> GetRecords(JObject jObject)
{
  return jObject["data"]
    .Children()
    .Select(CreateRecord)
    .ToList();
}

private (Class1, Class2) CreateRecord(JToken recordToken)
{
  var object1 = recordToken.ToObject<IClass1>();
  var object2 = recordToken.ToObject<IClass2>();
  return (object1, object2);
}

【问题讨论】:

  • 反序列化一次后为什么不直接复制集合?
  • 你能分享一下你为反序列化编写的类结构和代码吗?
  • 创建一个具有这两个属性的类型(或使用动态类型),反序列化,然后使用 LINQ 创建您需要的类型。
  • @Sinatr 看起来 OP 知道 DeserializeObject(StreamReader),因为他们在最后一段中提到它是一个理想的用例。他们使用string 只是因为这是他们知道的重用数据的唯一方法。在不知道如何通过单独的读取操作保持流打开的情况下,我可以看到问题。
  • 如果您想保留现有代码,可以使用JToken.Load(JsonReader)。您可以从StreamReader 创建一个JsonTextReader

标签: c# json.net


【解决方案1】:

我为您提供 3 条路径,从效率最低的开始,但代码最少。

直接从 Stream 反序列化为 JObject

这避免了首先将整个数据转换为字符串。从而节省了字符串的内存空间。

var serializer = new JsonSerializer();
using (var textReader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(textReader))
{
    var jobject = (JObject)serializer.Deserialize(jsonReader);
}

反序列化为 C# 类,然后创建您需要的类。 JObject 比 POCO 更重,消耗更多内存。

这是直截了当的。假设您创建的类是Data

var serializer = new JsonSerializer();
using (var textReader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(textReader))
{
    var data = serializer.Deserialize<Data>(jsonReader);
}

使用JsonTextReader直接读入你的目标数据结构

这涉及手动将 json 流的属性和值读取到您需要的结构中。它节省了任何中间对象的内存空间(或者更确切地说,任何中间对象将只包含您正在读取的当前令牌,而不是整个数据)。如果您返回IEnumerable&lt;(IClass1, IClass2)&gt;,您的方法的用户实际上可以以流式方式处理结果,从而使其成为online 算法。

我会让你深入研究文档以了解如何使用它:)

【讨论】:

  • 谢谢。我不知道JsonTextReader,但是当我检查它时,我决定我不会走这条乏味的道路。我太老了 :) 我可能会选择选项 #2,这也是 cmets 中其他人所建议的。但您的回答令人放心,我没有错过更好的解决方案。
猜你喜欢
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多