2017-02-14 108 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的用更少的中間表示的內容。

相關問題