Newtonsoft (json.net) 反序列化可以在 F# 中变得懒惰吗?
Can Newtonsoft (json.net) Deserialize be made lazy in F#?
考虑以下代码,它使用 FSharp.Data 从 Web 资源请求数据
let resp = Http.RequestStream(url, headers, query)
use rdr = new StreamReader(resp.ResponseStream)
use jrdr = new JsonTextReader(rdr)
let serializer = new JsonSerializer()
let myArray = serializer.Deserialize<someType[]>(jrdr).Value
myArray
是someType
的数组。数组被急切地评估,所以如果我请求大量数据,我会预先消耗大量 RAM。
如果我要求 json.net 给我一个序列呢?
let resp = Http.RequestStream(url, headers, query)
use rdr = new StreamReader(resp.ResponseStream)
use jrdr = new JsonTextReader(rdr)
let serializer = new JsonSerializer()
let mySeq = serializer.Deserialize<someType seq>(jrdr).Value
如果我遍历 mySeq 并将其写入文本文件,是否所有内容都从流中拉出并延迟反序列化?还是要求 json.net 反序列化的行为会强制在此时急切地评估所有内容?
更新
根据 dbc 的公认答案,功能性惰性函数将类似于以下内容
let jsonSeqFromStream<'T>(stream:Stream) = seq{
let serializer = JsonSerializer.CreateDefault()
use rdr = new StreamReader(stream, Encoding.UTF8, true, 4096, true)
use jrdr = new JsonTextReader(rdr, CloseInput = false)
let rec resSeq inArray = seq{
if jrdr.Read() then
match jrdr.TokenType with
|JsonToken.Comment -> yield! resSeq inArray
|JsonToken.StartArray when not inArray -> yield! resSeq true
|JsonToken.EndArray when inArray -> yield! resSeq false
|_ ->
let resObj = serializer.Deserialize<'T>(jrdr)
yield resObj
yield! resSeq inArray
else
()
}
yield! resSeq false
}
Json.NET 序列的反序列化可以变得惰性化,但不是那么自动。相反,您必须将 or 中的答案之一修改为 f#.
要确认序列的反序列化不是默认惰性的,定义以下函数:
let jsonFromStream<'T>(stream : Stream) =
Console.WriteLine(typeof<'T>) // Print incoming type for debugging purpose
let serializer = JsonSerializer.CreateDefault()
use rdr = new StreamReader(stream, Encoding.UTF8, true, 4096, true)
use jrdr = new JsonTextReader(rdr, CloseInput = false)
let res = serializer.Deserialize<'T>(jrdr)
Console.WriteLine(res.GetType()) // Print outgoing type for debugging purpose
res
然后如果我们有一些流 stream
包含一个 JSON 对象数组 someType
,并像这样调用方法:
let mySeq = jsonFromStream<someType seq>(stream)
然后生成以下调试输出:
System.Collections.Generic.IEnumerable`1[Oti4jegh9906+someType]
System.Collections.Generic.List`1[Oti4jegh9906+someType]
如您所见,从 .Net 的角度来看,用 someType seq
调用 JsonSerializer.Deserialize<T>()
与在 c# 中用 IEnumerable<someType>
调用它是一样的,在这样的case Json.NET 将结果具体化并 return 将其作为 List<someType>
.
演示 fiddle #1 here.
要将 JSON 数组解析为惰性序列,您需要手动创建一个 seq
函数,该函数用 JsonReader.Read()
遍历 JSON 并反序列化和产生每个数组条目:
let jsonSeqFromStream<'T>(stream : Stream) =
seq {
// Adapted from this answer
// To
let serializer = JsonSerializer.CreateDefault()
use rdr = new StreamReader(stream, Encoding.UTF8, true, 4096, true)
use jrdr = new JsonTextReader(rdr, CloseInput = false)
let inArray = ref false
while jrdr.Read() do
if (jrdr.TokenType = JsonToken.Comment) then
()
elif (jrdr.TokenType = JsonToken.StartArray && not !inArray) then
inArray := true
elif (jrdr.TokenType = JsonToken.EndArray && !inArray) then
inArray := false
else
let res = serializer.Deserialize<'T>(jrdr)
yield res
}
(因为跟踪我们是否正在解析数组值是有状态的,所以这看起来不太实用。也许它可以做得更好?)
这个函数的return可以这样使用,例如:
let mySeq = jsonSeqFromStream<someType>(stream)
mySeq |> Seq.iter (fun (s) -> printfn "%s" (JsonConvert.SerializeObject(s)))
演示 fiddle #2 here.
考虑以下代码,它使用 FSharp.Data 从 Web 资源请求数据
let resp = Http.RequestStream(url, headers, query)
use rdr = new StreamReader(resp.ResponseStream)
use jrdr = new JsonTextReader(rdr)
let serializer = new JsonSerializer()
let myArray = serializer.Deserialize<someType[]>(jrdr).Value
myArray
是someType
的数组。数组被急切地评估,所以如果我请求大量数据,我会预先消耗大量 RAM。
如果我要求 json.net 给我一个序列呢?
let resp = Http.RequestStream(url, headers, query)
use rdr = new StreamReader(resp.ResponseStream)
use jrdr = new JsonTextReader(rdr)
let serializer = new JsonSerializer()
let mySeq = serializer.Deserialize<someType seq>(jrdr).Value
如果我遍历 mySeq 并将其写入文本文件,是否所有内容都从流中拉出并延迟反序列化?还是要求 json.net 反序列化的行为会强制在此时急切地评估所有内容?
更新
根据 dbc 的公认答案,功能性惰性函数将类似于以下内容
let jsonSeqFromStream<'T>(stream:Stream) = seq{
let serializer = JsonSerializer.CreateDefault()
use rdr = new StreamReader(stream, Encoding.UTF8, true, 4096, true)
use jrdr = new JsonTextReader(rdr, CloseInput = false)
let rec resSeq inArray = seq{
if jrdr.Read() then
match jrdr.TokenType with
|JsonToken.Comment -> yield! resSeq inArray
|JsonToken.StartArray when not inArray -> yield! resSeq true
|JsonToken.EndArray when inArray -> yield! resSeq false
|_ ->
let resObj = serializer.Deserialize<'T>(jrdr)
yield resObj
yield! resSeq inArray
else
()
}
yield! resSeq false
}
Json.NET 序列的反序列化可以变得惰性化,但不是那么自动。相反,您必须将
要确认序列的反序列化不是默认惰性的,定义以下函数:
let jsonFromStream<'T>(stream : Stream) =
Console.WriteLine(typeof<'T>) // Print incoming type for debugging purpose
let serializer = JsonSerializer.CreateDefault()
use rdr = new StreamReader(stream, Encoding.UTF8, true, 4096, true)
use jrdr = new JsonTextReader(rdr, CloseInput = false)
let res = serializer.Deserialize<'T>(jrdr)
Console.WriteLine(res.GetType()) // Print outgoing type for debugging purpose
res
然后如果我们有一些流 stream
包含一个 JSON 对象数组 someType
,并像这样调用方法:
let mySeq = jsonFromStream<someType seq>(stream)
然后生成以下调试输出:
System.Collections.Generic.IEnumerable`1[Oti4jegh9906+someType] System.Collections.Generic.List`1[Oti4jegh9906+someType]
如您所见,从 .Net 的角度来看,用 someType seq
调用 JsonSerializer.Deserialize<T>()
与在 c# 中用 IEnumerable<someType>
调用它是一样的,在这样的case Json.NET 将结果具体化并 return 将其作为 List<someType>
.
演示 fiddle #1 here.
要将 JSON 数组解析为惰性序列,您需要手动创建一个 seq
函数,该函数用 JsonReader.Read()
遍历 JSON 并反序列化和产生每个数组条目:
let jsonSeqFromStream<'T>(stream : Stream) =
seq {
// Adapted from this answer
// To
let serializer = JsonSerializer.CreateDefault()
use rdr = new StreamReader(stream, Encoding.UTF8, true, 4096, true)
use jrdr = new JsonTextReader(rdr, CloseInput = false)
let inArray = ref false
while jrdr.Read() do
if (jrdr.TokenType = JsonToken.Comment) then
()
elif (jrdr.TokenType = JsonToken.StartArray && not !inArray) then
inArray := true
elif (jrdr.TokenType = JsonToken.EndArray && !inArray) then
inArray := false
else
let res = serializer.Deserialize<'T>(jrdr)
yield res
}
(因为跟踪我们是否正在解析数组值是有状态的,所以这看起来不太实用。也许它可以做得更好?)
这个函数的return可以这样使用,例如:
let mySeq = jsonSeqFromStream<someType>(stream)
mySeq |> Seq.iter (fun (s) -> printfn "%s" (JsonConvert.SerializeObject(s)))
演示 fiddle #2 here.