C# 验证流中的JSON
我上传了(可能很大)json文件,需要在其他地方写出。我希望至少做一些基本的验证(例如,确保它们是有效的JSON,甚至可能应用模式),但我希望避免将整个(同样,可能很大)文件加载到内存中,然后再将其写入。我使用的是JSON.Net,我想我可以这样做:C# 验证流中的JSON,c#,json,validation,json.net,C#,Json,Validation,Json.net,我上传了(可能很大)json文件,需要在其他地方写出。我希望至少做一些基本的验证(例如,确保它们是有效的JSON,甚至可能应用模式),但我希望避免将整个(同样,可能很大)文件加载到内存中,然后再将其写入。我使用的是JSON.Net,我想我可以这样做: using (var sr = new StreamReader(source)) using (var jsonReader = new JsonTextReader(sr)) using (var textWriter = new Stream
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);
}
}
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [125.6, 10.1]
},
"properties": {
"name": "Dinagat Islands"
}
}
其思想是,读取器将在JSON文件进入时遍历它,并在处理每个令牌时将其写出。如果输入中有错误,它将抛出一个异常,我可以通过向用户返回错误消息来处理该异常
问题是,如果我使用一个JSON文件来逐步完成这段代码,该文件由一个具有数组属性的单个对象组成,该数组属性包含了更多对象的集合(整个文件的格式约为1.3k行),我希望它能够逐步完成。相反,它似乎只是读取整个对象,然后一步将其再次吐出
有没有一种方法可以处理steam中的大型JSON对象,确保它们确实是有效的JSON,并将它们流式输出,而不必一次将整个对象保存在内存中)
虽然答案可能更一般,但我目前试图处理的数据是GeoJson数据。一个(非常简短)的示例如下所示:
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);
}
}
{
"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
]
}
这里的建议是:
它应该读取单个标记
StartObject
,PropertyName
,等等。如果您希望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);
var myDeserializedJSON=JsonConvert.DeserializeObject(jsonData);
至少要部分回答我自己的问题,问题在于:
outputStream.WriteToken(jsonReader);
事实证明,它写的是代币和它的所有子代。我想这意味着它基本上读取整个文件。第一个标记是StartObject
,通过将它的所有子对象都写出来,它必须一直读到EndObject
标记
使用:
outputStream.WriteToken(jsonReader, false);
不会自动读取所有的子项,而是逐个令牌地执行,我猜(希望)对于非常大的文件,这将更加节省内存
仍然不能100%确定这是否是最有效的解决方案,除了确保它是有效的JSON之外,最好至少做一点验证。如果您可以将流拉两次(避免直接拉入内存)或将流保存为文件,以便从中创建多个流,使用
jschemavalidationreader
并在读取时使用空while循环。JSChemaValidationReader将遍历整个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
}
}
JsonValidatingReader
之后的schema
是您要验证的模式。您必须在扩展JsonValidator
的类中执行任何自定义验证,您可以看到如何执行。验证后,您可以从远程源或文件中提取流 这需要加载整个文件。我不想反序列化,我只想验证并传递它。不加载整个文件。@MattBurland:如果没有全部检查,则无法检查整个文件。@MattBurland,如果没有全部检查,您将如何验证它?与反序列化时首先验证文件的方式相同。您检查它是否以{
后接括号中的属性名,后接:
后接值、对象或数组…如果您描述的是要验证的方式,则只读取前x行,直到您可以完成验证。然后,如果验证通过,则重新启动读取,并按照执行验证之前的方式使用对象ion.你能举一个JSON文件的例子吗?如果整个文件只包含一个对象,那么在不读取整个文件的情况下,真的没有办法验证该对象。如果文件由多个对象组成,你可以定义单个对象的开始/停止元素并读取这些块。这样做也适合answer提供了反序列化到预定义类的功能。而且,1.3k行实际上并没有那么大,如果你将它反序列化到一个对象,该对象实际消耗了多少内存?你可能会从一些不需要的东西中产生问题。@Jeremy Whoops。说得好。我忘了我们最初收到两次流来处理这个问题。交易禁令dwidth,以防止将其拉入内存,因为流非常大。我们后来只是将流保存到一个使用磁盘空间而不是内存的文件中,因为我们将所有文件处理都移动到本地。