JSON DeserializeObject非常慢

时间:2016-06-14 16:07:06

标签: c# json json.net

我创建了一个小的递归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 { }
    }

1 个答案:

答案 0 :(得分:1)

由于您没有包含实际JSON的示例,因此您的问题并不是很清楚,但是您似乎正在尝试按顺序反序列化大型JSON数组中的值或值大型JSON对象中的键/值对存储在磁盘上的文件中。

话虽如此,可以提出一些建议:

  1. 不应将JSON加载到大型(3.5 MB)字符串中,而应直接从文件中流式传输,如Performance Tips: Optimize Memory Usage中所述。

  2. 您当前的方法似乎是反序列化为大型临时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);
                    }
                }
            }
        }
    }
    
  3. 如果您尝试反序列化的值是基元(即只是字符串,如示例所示),那么您应该完全跳过反序列化,并直接读取它们。反序列化需要创建和处理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.
                }
            }
        }
    }
    
  4. 对于#2和#3,您可以通过在磁盘上打开文件来传递StreamStreamReader