我创建了一个小的递归C#app来解析未知的JSON字符串,以使用Newtonsoft.Json.dll
查找特定的键/值对。它在小JSON字符串上运行正常,但是如果JSON更大则需要很长时间:3.5MB带有15K +行的JSON文件需要> 3分钟才能解析。使用RegExp解析相同的文件需要<1秒。是JsonConvert.DeserializeObject()
需要那么久吗?!
string json = @"{""origin-of_error"" : ""error_message"",""foo"" : ""bar""}";
static void GetJsonValue (string json, string findStr = "foo")
{
try
{
if (Regex.Match(json, @"^\[", RegexOptions.Multiline).Success)
{
// JSON string Array []
var jArr = JsonConvert.DeserializeObject<List<Object>>(json);
foreach (var jLine in jArr) GetJsonValue(jLine.ToString());
}
else
{
// JSON string KEY:VALUE
var jLog = JsonConvert.DeserializeObject<Dictionary<String, Object>>(json);
foreach (KeyValuePair<string, object> jEntry in jLog)
{
if (jEntry.Key.ToString() == findStr) Console.WriteLine("MATCH:" + jEntry.ToString());
GetJsonValue(jEntry.Value.ToString());
}
}
}
catch { }
}
答案 0 :(得分:1)
由于您没有包含实际JSON的示例,因此您的问题并不是很清楚,但是您似乎正在尝试按顺序反序列化大型JSON数组中的值或值大型JSON对象中的键/值对存储在磁盘上的文件中。
话虽如此,可以提出一些建议:
不应将JSON加载到大型(3.5 MB)字符串中,而应直接从文件中流式传输,如Performance Tips: Optimize Memory Usage中所述。
您当前的方法似乎是反序列化为大型临时Dictionary<string, object>
或List<object>
,然后对每个值重新序列化为字符串,并对其进行反序列化。这不会有效。
如果您尝试反序列化的值是复杂对象,则可以从Parsing large json file in .NET调整解决方案,增强以处理您的根JSON容器可能是数组或对象的事实。
因此,请使用:
public static partial class JsonExtensions
{
public static IEnumerable<T> DeserializeValues<T>(Stream stream)
{
return DeserializeValues<T>(new StreamReader(stream));
}
public static IEnumerable<T> DeserializeValues<T>(TextReader textReader)
{
var serializer = JsonSerializer.CreateDefault();
var reader = new JsonTextReader(textReader);
reader.SupportMultipleContent = true;
while (reader.Read())
{
if (reader.TokenType == JsonToken.StartArray)
{
while (reader.Read())
{
if (reader.TokenType == JsonToken.Comment)
continue; // Do nothing
else if (reader.TokenType == JsonToken.EndArray)
break; // Break from the loop
else
yield return serializer.Deserialize<T>(reader);
}
}
else if (reader.TokenType == JsonToken.StartObject)
{
while (reader.Read())
{
if (reader.TokenType == JsonToken.Comment)
continue; // Do nothing
else if (reader.TokenType == JsonToken.PropertyName)
continue; // Eat the property name
else if (reader.TokenType == JsonToken.EndObject)
break; // Break from the loop
else
yield return serializer.Deserialize<T>(reader);
}
}
}
}
}
如果您尝试反序列化的值是基元(即只是字符串,如示例所示),那么您应该完全跳过反序列化,并直接读取它们。反序列化需要创建和处理data contract,通常通过反射。阅读直接消除了这种复杂性。
因此你可以这样做:
public static partial class JsonExtensions
{
public static bool IsPrimitive(this JsonToken tokenType)
{
switch (tokenType)
{
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Undefined:
case JsonToken.Null:
case JsonToken.Date:
case JsonToken.Bytes:
return true;
default:
return false;
}
}
public static IEnumerable<string> ReadPrimitives(Stream stream)
{
return ReadPrimitives(new StreamReader(stream));
}
public static IEnumerable<string> ReadPrimitives(TextReader textReader)
{
var reader = new JsonTextReader(textReader);
reader.SupportMultipleContent = true;
while (reader.Read())
{
if (reader.TokenType.IsPrimitive())
{
if (reader.TokenType == JsonToken.String)
yield return reader.Value.ToString(); // No need for conversion
else
yield return (string)JValue.Load(reader); // Convert to string.
}
}
}
}
对于#2和#3,您可以通过在磁盘上打开文件来传递Stream
或StreamReader
。