[英]Validating JSON in a stream
我有(可能很大)正在上传的 json 文件,需要在其他地方写出。 我想至少做一些基本的验证(例如,确保它们是有效的 JSON - 甚至可能应用一个模式)但我想避免必须将整个(同样,可能很大)文件加载到内存中,然后必须重新写出来。 我正在使用 JSON.Net 并认为我可以做这样的事情:
using (var sr = new StreamReader(source))
using (var jsonReader = new JsonTextReader(sr))
using (var textWriter = new StreamWriter(myoutputStream))
using (var outputStream = new JsonTextWriter(textWriter))
{
while (jsonReader.Read())
{
// TODO: any addition validation!
outputStream.WriteToken(jsonReader);
}
}
其想法是读者将在 JSON 文件进入时对其进行遍历,并在处理每个令牌时将其写出。 如果输入中有错误,它会抛出一个异常,我可以通过向用户返回错误消息来处理该异常。
问题是,如果我使用一个 JSON 文件逐步执行此代码,该文件由一个具有数组属性的单个对象组成,该数组属性具有更多对象的集合(整个文件的格式约为 1.3k 行),我希望它能够逐步执行。 相反,它似乎只是读入了整个对象,然后一步又将其吐出。
有没有办法从蒸汽中处理大型 JSON 对象,确保它们确实是有效的 JSON 并将它们流出来,而不必一次将整个对象保存在内存中)。
虽然答案可能更笼统,但我目前尝试处理的数据是 GeoJson 数据。 一个(非常短的)示例如下所示:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [125.6, 10.1]
},
"properties": {
"name": "Dinagat Islands"
}
}
一个更长的例子可能是:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Van Dorn Street",
"marker-color": "#0000ff",
"marker-symbol": "rail-metro",
"line": "blue"
},
"geometry": {
"type": "Point",
"coordinates": [
-77.12911152370515,
38.79930767201779
]
}
},...//lots more objects
]
}
来自这里的建议: https ://www.newtonsoft.com/json/help/html/ReadingWritingJSON.htm
它是否应该读取单个令牌StartObject
、 PropertyName
等...
至少部分回答我自己的问题,问题在这里:
outputStream.WriteToken(jsonReader);
事实证明,其中写入了令牌及其所有子代。 我认为这意味着它基本上读取整个文件。 第一个标记将是一个StartObject
,通过写出它的所有子项,它必须一直读取到EndObject
标记。
使用:
outputStream.WriteToken(jsonReader, false);
不会自动读取所有子项,而是逐个逐个标记,我猜(希望)对于非常大的文件来说,内存效率更高。
仍然不能 100% 确定这是否是最有效的解决方案,除了确保它是有效的 JSON 之外,至少做一点验证会很好。
如果您可以两次拉取流(以避免直接拉入内存)或将流保存为文件以便从中创建多个流,请使用JSchemaValidatingReader
并在读取时使用空的 while 循环。 JschemaValidatingReader 将遍历整个 JSON 而不会进入内存。
using (var stream = fileStream.Stream)
using (var streamReader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(streamReader))
using (var validatingReader = new JSchemaValidatingReader(jsonReader) { Schema = schema })
{
validatingReader.ValidationEventHandler += (o, a) =>
{
// log or output the validation errors that come up here
};
while (validatingReader.Read())
{
// Do nothing here - forces reader through the stream and validates
}
}
schema
后JsonValidatingReader
是你对验证的架构。 您必须在扩展JsonValidator
的类中执行任何自定义验证,您可以在此处了解如何执行。 验证后,您将从远程源或文件中提取流。
我有类似的要求,我需要解析 JSON 文件并提取数据的“模式”或形状。 虽然处理小型文档(在内存序列化中使用)很简单,但事实证明处理大型 JSON 文件很困难,因为您很容易遇到内存异常。
经过多次挠头并考虑了这个答案中的实现,下面的代码将流式传输 JSON 数据并构建一个示例 json 文件,当它遇到数组时,它只考虑第一项。 这样我就可以从现有的 JSON 中高效地生成 JSON“模式”。 我努力寻找其他可以从现有 JSON 生成模式的解决方案,而无需先将数据加载到内存中。
//https://stackoverflow.com/questions/43747477/how-to-parse-huge-json-file-as-stream-in-json-net
//https://stackoverflow.com/questions/49241890/validating-json-in-a-stream
/// <summary>
/// Generates sample json from raw json.
/// When generator encounters arrays, will only consider the first item.
/// </summary>
/// <param name="inputStream"></param>
/// <returns></returns>
/// <exception cref="Newtonsoft.Json.JsonReader">Throws if input stream is invalid json.</exception>
private async static Task<Stream> GenerateJSONSchemaAsync(Stream inputStream)
{
MemoryStream myOutputStream = new MemoryStream();
using (StreamReader sr = new StreamReader(inputStream))
using (JsonReader reader = new JsonTextReader(sr))
using (StreamWriter textWriter = new StreamWriter(myOutputStream))
using (JsonTextWriter outputStream = new JsonTextWriter(textWriter))
{
while (await reader.ReadAsync())
{
//If path contains [x] ignore if [x] index is > 0 (ie > than first child)
MatchCollection match = Regex.Matches(reader.Path, @"(?<=\[).+?(?=\])");
if (!match.Any() || !match.Any(x => int.Parse(x.Value) > 0))
{
await outputStream.WriteTokenAsync(reader, false);
}
}
}
return myOutputStream;
}
如果您希望您的 JSON 文件具有某种标准化结构。 然后,您可以创建一个具有相同属性的类,然后将 JSON 文件反序列化为正确的类。
如果 JSON 格式有效,则会发生反序列化。 例如:
[JsonObject]
public class MyClass
{
[JsonProperty("id")]
public string Id {get; set;}
[JsonProperty("name")]
public string Name { get; set; }
public MyClass() { }
}
然后通过此调用反序列化 JSON:
var myDeserializedJSON= JsonConvert.DeserializeObject<MyClass>(jsonData);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.