2017-02-14 77 views
0

这里有什么非常无效的东西吗?看起来这个过程正在走的更远。我解析许多JSON文件,每个JsonArray的对象。也许有更多经验的人可能会指出这种将JSON解析为对象的方法中的错误,从而为我节省了大量时间。使用Newtonsoft ToObject反序列化许多Json文件

此外,内存使用量慢慢MB有时会导致outofmemoryexceptions悄悄向上MB ..

public void Parse(){ 

    using (BabysFirstsUsersDataEntities db = new BabysFirstsUsersDataEntities() 
    { 

      foreach (var path in Directory.EnumerateFiles(@"C:\\examplepath\").OrderBy(f => f)) 
      { 
        string jsonString = System.IO.File.ReadAllText(path); 
        JToken tok = JObject.Parse(jsonString); 
        Debug.WriteLine("path: " + path); 

        foreach (var x in tok.First.First) 
        { 

          JsonUserImageDTO jdto = x.ToObject<JsonUserImageDTO>(); 
          UserImageList uil = jdto.ToDataModel(); 

          if (uil.REID != null) 
           db.UserImageLists.Add(uil);  

        } 
      } 
     db.SaveChanges();    
    } 
} 

的东西在每个以.json文件中的JSON字符串中的一个看起来像下面的一个例子。请注意,有这些文件的1000左右每个人都可以有成千上万这样的条目:

{ 
    "results": [ 
    { 
     "ACL": { 
     "asdf": { 
      "read": true, 
      "write": true 
     }, 
     "role:admin": { "read": true } 
     }, 
     "REID": "exampleID", 
     "createdAt": "datetime-string", 
     "email": "example", 
     "objectId": "example", 
     "updatedAt": "datetimestring", 
     "urlCount": 1, 
     "urlList": [ "exampleurl" ] 
    }, 
    { 
     "ACL": { 
     "asdf": { 
      "read": true, 
      "write": true 
     }, 
     "role:admin": { "read": true } 
     }, 
     "REID": "exampleID", 
     "createdAt": "datetime-string", 
     "email": "example", 
     "objectId": "example", 
     "updatedAt": "datetimestring", 
     "urlCount": 1, 
     "urlList": [ "exampleurl" ] 
    } 
    ] 
    } 
+1

1)您应该直接从'Stream'加载(http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Linq_JToken_Load.htm),而不是加载到大的中间字符串,然后解析字符串,正如[这里](http://www.newtonsoft.com/json/help/html/Performance.htm)所解释的那样。 2)为什么内部循环内部的path.Contains(“UserImageList”)?如果这不是真的,你不能完全跳过文件吗? – dbc

+0

我将签出#1并回报...#2指的是文件的名称,它是我执行时处理的不同文件类型对应于不同模型的剩余部分。我现在会改变它,因为它使事情看起来比它们更复杂 – Jaked222

+0

像这样? StreamReader sr = new StreamReader(path); JsonReader reader = new JsonTextReader(sr); var tok = JObject.Load(reader); – Jaked222

回答

0

您可以创建对象,然后反序列化它们。 例如:

JsonConvert.DeserializeObject<RootObject>(jsonString); 

public class Asdf 
{ 
    public bool read { get; set; } 
    public bool write { get; set; } 
} 

public class RoleAdmin 
{ 
    public bool read { get; set; } 
} 

public class ACL 
{ 
    public Asdf asdf { get; set; } 
    public RoleAdmin { get; set; } 
} 

public class Result 
{ 
    public ACL ACL { get; set; } 
    public string REID { get; set; } 
    public string createdAt { get; set; } 
    public string email { get; set; } 
    public string objectId { get; set; } 
    public string updatedAt { get; set; } 
    public int urlCount { get; set; } 
    public List<string> urlList { get; set; } 
} 

public class RootObject 
{ 
    public List<Result> results { get; set; } 
} 
+1

这几乎是JSONUserImageDTO和UserImage类正在做什么 – Jaked222

+0

@ Jaked222-确定,做什么性能这样而不是你的方式? – dbc

1

看起来可能有几个地方可能导致缓慢。

  1. Deserialzing JSON
  2. 改造对象两次(jdto,然后uil
  3. 保存到数据库

这可能是值得剖析的代码来找出到底是哪个部分需要较长时间比你想象的要多。这就是说你可以做一些事情来改进这个代码。

  1. 从蒸汽而不是字符串反序列化。你拥有它的方式,你基本上在内存中的对象两次 - 一次作为一个字符串,然后一次为tok。有关如何使用流的信息,请参阅第二个示例in the docs。其实,在你的情况下,你在内存中的相同信息4次 - 字符串,tok,jdtouil。这将我带到下一点..
  2. 尝试消除您的对象的一些中间表示。一般来说,你放置的物体越多,你等待GC的时间就越多。
  3. 将路径名称上的过滤移至您拨打EnumerateFiles()的部分。如果您不打算对它进行任何操作,则将文件反序列化是没有意义的。
+0

#1:这样的事情? StreamReader sr = new StreamReader(path); JsonReader reader = new JsonTextReader(sr); var tok = JObject.Load(reader); – Jaked222

+1

是的,但不要忘记'using'语句。 –

+1

即使orderby会很慢,如果有很多文件。 –

1

您是否真的对您的代码进行了分析?请参阅Erik Lippert的performance rant使用分析器或其他分析工具在开始调查替代方法之前根据经验确定瓶颈的位置。例如,您的实际性能问题可能在BabysFirstsUsersDataEntities db类中。

这就是说,我的直接反应是你有太多的数据中间表示,建设,人口和垃圾收集都需要时间。这些措施包括:

  • 可能大到足以走在large object heap,从而永久性地损害性能和内存使用的过程的jsonString
  • 您的整个JSON层次结构的JToken tok表示形式。每个人JsonUserImageDTO

我建议的是尽可能多地消除这些中间表示。如the documentation中所建议的,您应该直接从流中加载,而不是加载到字符串并解析该字符串。

您也可以通过直接填充数据模型来消除JToken tok。比方说,你的BabysFirstsUsersDataEntities看起来像这样(我只是猜测这里):

public class BabysFirstsUsersDataEntities 
{ 
    public BabysFirstsUsersDataEntities() { this.UserImageLists = new List<UserImageList>(); } 

    public List<UserImageList> UserImageLists { get; set; } 
} 

public class UserImageList 
{ 
    public string email { get; set; } 
    public List<string> urlList; 
} 

而且你的DTO模式看起来是这样的模型由http://json2csharp.com/提供:

public class RootObjectDTO 
{ 
    public ICollection<JsonUserImageDTO> results { get; set; } 
} 

public class JsonUserImageDTO 
{ 
    public ACL ACL { get; set; } 
    public string REID { get; set; } 
    public string createdAt { get; set; } 
    public string email { get; set; } 
    public string objectId { get; set; } 
    public string updatedAt { get; set; } 
    public int urlCount { get; set; } 
    public List<string> urlList { get; set; } 

    public UserImageList ToDataModel() 
    { 
     return new UserImageList { email = email, urlList = urlList }; 
    } 
} 

public class Asdf 
{ 
    public bool read { get; set; } 
    public bool write { get; set; } 
} 

public class RoleAdmin 
{ 
    public bool read { get; set; } 
} 

public class ACL 
{ 
    public Asdf asdf { get; set; } 

    [JsonProperty("role:admin")] 
    public RoleAdmin RoleAdmin { get; set; } 
} 

然后创建下列通用ConvertingCollection<TIn, TOut>工具类:

public class ConvertingCollection<TIn, TOut> : BaseConvertingCollection<TIn, TOut, ICollection<TIn>> 
{ 
    readonly Func<TOut, TIn> toInner; 

    public ConvertingCollection(Func<ICollection<TIn>> getCollection, Func<TIn, TOut> toOuter, Func<TOut, TIn> toInner) 
     : base(getCollection, toOuter) 
    { 
     if (toInner == null) 
      throw new ArgumentNullException(); 
     this.toInner = toInner; 
    } 

    protected TIn ToInner(TOut outer) { return toInner(outer); } 

    public override void Add(TOut item) 
    { 
     Collection.Add(ToInner(item)); 
    } 

    public override void Clear() 
    { 
     Collection.Clear(); 
    } 

    public override bool IsReadOnly { get { return Collection.IsReadOnly; } } 

    public override bool Remove(TOut item) 
    { 
     return Collection.Remove(ToInner(item)); 
    } 

    public override bool Contains(TOut item) 
    { 
     return Collection.Contains(ToInner(item)); 
    } 
} 

public abstract class BaseConvertingCollection<TIn, TOut, TCollection> : ICollection<TOut> 
where TCollection : ICollection<TIn> 
{ 
    readonly Func<TCollection> getCollection; 
    readonly Func<TIn, TOut> toOuter; 

    public BaseConvertingCollection(Func<TCollection> getCollection, Func<TIn, TOut> toOuter) 
    { 
     if (getCollection == null || toOuter == null) 
      throw new ArgumentNullException(); 
     this.getCollection = getCollection; 
     this.toOuter = toOuter; 
    } 

    protected TCollection Collection { get { return getCollection(); } } 

    protected TOut ToOuter(TIn inner) { return toOuter(inner); } 

    #region ICollection<TOut> Members 

    public abstract void Add(TOut item); 

    public abstract void Clear(); 

    public virtual bool Contains(TOut item) 
    { 
     var comparer = EqualityComparer<TOut>.Default; 
     foreach (var member in Collection) 
      if (comparer.Equals(item, ToOuter(member))) 
       return true; 
     return false; 
    } 

    public void CopyTo(TOut[] array, int arrayIndex) 
    { 
     foreach (var item in this) 
      array[arrayIndex++] = item; 
    } 

    public int Count { get { return Collection.Count; } } 

    public abstract bool IsReadOnly { get; } 

    public abstract bool Remove(TOut item); 

    #endregion 

    #region IEnumerable<TOut> Members 

    public IEnumerator<TOut> GetEnumerator() 
    { 
     foreach (var item in Collection) 
      yield return ToOuter(item); 
    } 

    #endregion 

    #region IEnumerable Members 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    #endregion 
} 

现在,您可以填充你的db直接如下:

 var rootDTO = new RootObjectDTO 
     { 
      results = new ConvertingCollection<UserImageList, JsonUserImageDTO>(() => db.UserImageLists, (x) => { throw new NotImplementedException(); }, (x) => x.ToDataModel()) 
     }; 

     using (var stream = File.Open(path, FileMode.Open)) 
     using (var reader = new StreamReader(stream)) 
     { 
      JsonSerializer.CreateDefault().Populate(reader, rootDTO); 
     } 

通过填充预分配rootDTOConvertingCollection<UserImageList, JsonUserImageDTO>,你db.UserImageLists将得到填充了JSON的用更少的中间表示的内容。

相关问题