緊跟上篇文章 十個程式開啟十個bash後一致寫入命令執行完畢之後產生了很多很多的檔案,博主需要對這些檔案同意處理,也就是說對幾十萬個檔案進行處理,想了又想,單執行緒處理那麼多資料肯定不行,於是乎想到了使用多執行緒,緊接著就引發了一系列問題,其中做大的問題就是json序列化,導致了多條執行緒執行和單執行緒執行時間一致問題。
我們正常去讀取json檔案轉成一般是通過實體類去使用JsonConvert.DeserializeObject方法進行接收的,然後再通過實體類去進行一系列的操作,目前遇到的問題就是讀取上萬的json檔案進行反序列化與序列化進行操作,如果一條一條的去操作的話速度可謂是非常非常慢,然後經過大佬的推薦和自己瞭解決定使用微軟專門推出的一個操作json的類。
這個類說的也非常清楚,提供高效能、低分配和標準相容的功能,以處理 JavaScript 物件表示法 (JSON),其中包括將物件序列化為 JSON 文字以及將 JSON 文字反序列化為物件(內建 UTF-8 支援)。 它還提供型別以用於讀取和寫入編碼為 UTF-8 的 JSON 文字,以及用於建立記憶體中文件物件模型 (DOM) 以在資料的結構化檢視中隨機訪問 JSON 元素。
雖然這個類庫專門針對於效能來優化的,但是使用起來往往非常的困難,沒有我們大眾所使用的JsonConvert.DeserializeObject方便,但是如像我一樣對待效能有極致的要求的話,可以使用這個類庫,用法也非常簡單,下面我給出例子
引入名稱空間:
System.Text.Json
那麼具體如何使用呢?我這裡給出具體的使用方法以及詳細的例子。
JsonDocument document1 = null; StreamReader f2 = new StreamReader(filePath, Encoding.UTF8); String line; while ((line = f2.ReadLine()) != null) { document1 = JsonDocument.Parse(line); } f2.Close(); f2.Dispose();
這裡我們去讀取filepath,filepath是json檔案路徑。我們使用 JsonDocument.Parse對json檔案進行操作。這個方法表示單個 JSON 位元組值的 UTF-8 編碼文字形式的序列分析為 JsonDocument。
把json檔案序列分析為JsonDocument型別了下面就是對這個型別進行操作,為什麼現在好多人不喜歡使用這種方法,大概率是因為取值比較難
對於JsonDocument如何取出我們想要的值或者節點呢?看下面的例子:
{ "ClassName": "Science", "Teacher\u0027s Name": "Jane", "Semester": "2019-01-01", "Students": [ { "Name": "John", "Grade": 94.3 }, { "Name": "James", "Grade": 81.0 }, { "Name": "Julia", "Grade": 91.9 }, { "Name": "Jessica", "Grade": 72.4 }, { "Name": "Johnathan" } ], "Final": true }
上述是一個json檔案內的資料,我們解析JsonDocument如何解析出Class Name節點值呢?
可以使用如下操作
//定義變數去接收 var className = "0"; //判斷我們解析出來的JsonDocument是否為空 if (document1 != null) { JsonElement root = document1.RootElement; className = root.TryGetProperty("ClassName", out var temp) ? temp.GetInt32().ToString() : "0"; }
首先使用 JsonDocument.RootElement屬性 獲取此 JSON 文件的根元素。
對於根元素進行解析,JsonElemet.TryGetProperty() 查詢當前物件中名為 ClassName 的屬性,返回一個指示此類屬性是否存在的值。 如果此屬性存在,會將其值分配給 value
引數。現在value引數對應temp,解析時還應注意屬性值型別,如果為string型別則使用GetString(),去進行轉換,int型別可以使用GetInt32(),進行轉換,具體其他型別可以檢視微軟官方給出的型別。
使用TryGetProperty()方法有什麼好處呢?
可以去判斷json檔案內有沒有當前的屬性,如果有的話,去返回屬性值,無,則返回null。
現在我們取單獨屬性已經會了,那麼如何取陣列呢?如何取陣列物件呢?
例子:
double sum = 0; int count = 0; using (JsonDocument document = JsonDocument.Parse(jsonString)) { JsonElement root = document.RootElement; JsonElement studentsElement = root.GetProperty("Students"); count = studentsElement.GetArrayLength(); foreach (JsonElement student in studentsElement.EnumerateArray()) { if (student.TryGetProperty("Grade", out JsonElement gradeElement)) { sum += gradeElement.GetDouble(); } else { sum += 70; } } } double average = sum / count; Console.WriteLine($"Average grade : {average}");
上面這個例子是微軟官方給出的例子,但是使用與我們已經知道json檔案格式的情況。
使用之前我們可以使用TryGetProperty方法先判斷json屬性是否存在,存在的話去定義一個value值進行接收,先判斷是否為存在,存在則把value值進行遍歷,然後再去判斷這個陣列內的屬性是否存在,存在的話返回屬性值,以此類推便可以拿到所有的json檔案中的屬性值啦!
效能提升:說了這麼多到底這種方法能夠提升多少速度呢,經過博主測試JsonConvert.DeserializeObject方法線上程或者程式內使用的話開啟多個和單個執行緒(程式)速度相差基本不大,就好比我開了十個執行緒,去讀取十萬個檔案,結果和單個執行緒去讀取十萬個檔案相差幾秒??是不是非常離譜。
換用這種高效能處理json檔案的類庫之後,開啟十個執行緒去操作檔案速度直接減少了三分之二!!!!!!