优化c#自定义Xml解析器类

本文关键字:Xml 自定义 优化 | 更新日期: 2023-09-27 18:06:50

我正在尝试编写一个XML解析器来解析大学提供的给定日历年和学期的所有课程。特别是,我想要得到系首字母缩略词(例如FIN代表金融等),课程编号(例如数学415,415将是数字),课程名称和课程值的学分数。

我试图解析的文件可以在这里找到

编辑和更新

在读者深入了解XML解析和优化它的最佳方法时,我偶然发现了这个博客POST

假设那篇文章中运行的测试结果既真实又准确,那么XmlReader的性能似乎远远超过XDocument和XmlDocument,这证实了下面精彩答案中所说的内容。话虽如此,我使用XmlReader重新编码了解析器类,并限制了单个方法中使用的读取器的数量。

下面是新的解析器类:
        public void ParseDepartments()
        {
            // Create reader for the given calendar year and semester xml file
            using (XmlReader reader = XmlReader.Create(xmlPath)) {
                reader.ReadToFollowing("subjects");  // Navigate to the element 'subjects'
                while (!reader.EOF) {
                    string pth = reader.GetAttribute("href");  // Get department's xml path
                    string acro = reader.GetAttribute("id");  // Get the department's acronym
                    reader.Read();  // Read through current element, ensures we visit each element
                    if (acro != null && acro != string.Empty) {  // If the acronym is valid, add it to the department list
                        deps.AddDepartment(acro, pth);
                    }
                }
            }
        }
        public void ParseDepCourses()
        {
            //  Loop through all the departments, and visit there respective xml file
            foreach (KeyValuePair<string, string> department in deps.DepartmentPaths) {
                try {
                    using (XmlReader reader = XmlReader.Create(department.Value)) {
                        reader.ReadToFollowing("courses");  // Navigate to the element 'courses'
                        while (!reader.EOF) {
                            string pth = reader.GetAttribute("href");
                            string num = reader.GetAttribute("id");
                            reader.Read();
                            if (num != null && num != string.Empty) {
                                string crseName = reader.Value;  //  reader.Value is the element's value, i.e. <elementTag>Value</elementTag> 
                                deps[department.Key].Add(new CourseObject(num, crseName, termID, pth)); // Add the course to the department's course list
                            }
                        }
                    }
                } catch (WebException) { }  // WebException is thrown (Error 404) when there is no xml file found, or in other words, the department has no courses
            }
        }
        public void ParseCourseInformation()
        {
            Regex expr = new Regex(@"^'S(L*)'d'b|^'S(L*)'b|^'S'd'b|^'S'b", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);  // A regular expression that will check each section and determine if it is a 'Lecture' section, at which point, that section's xml file is visited, and instructor added
            foreach (KeyValuePair<string, Collection<CourseObject>> pair in deps) {
                foreach (CourseObject crse in pair.Value) {
                    try {
                        using (XmlReader reader = XmlReader.Create(crse.XmlPath)) {
                            reader.ReadToFollowing("creditHours");  // Get credit hours for the course
                            crse.ParseCreditHours(reader.Value);  //  Class method to parse the string and grab the correct integer values
                            reader.ReadToFollowing("sections"); //  Navigate to the element 'sections'
                            while (!reader.EOF) {
                                string pth = reader.GetAttribute("href");
                                string crn = reader.GetAttribute("id");
                                reader.Read();
                                if (crn != null && crn != string.Empty) {
                                    string sction = reader.Value;
                                    if (expr.IsMatch(sction)) {  // Check if sction is a 'Lecture' section
                                        using (XmlReader reader2 = XmlReader.Create(pth)) {  //  Navigate to its xml file
                                            reader2.ReadToFollowing("instructors");  // Navigate to the element 'instructors'
                                            while (!reader2.EOF) {
                                                string firstName = reader2.GetAttribute("firstName");
                                                string lastName = reader2.GetAttribute("lastName");
                                                reader2.Read();
                                                if ((firstName != null && firstName != string.Empty) && (lastName != null && lastName != string.Empty)) { //  Check and make sure its a valid name
                                                    string instr = firstName + ". " + lastName;  //  Concatenate into full name
                                                    crse.AddSection(pth, sction, crn, instr);  //  Add section to course
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } catch (WebException) { }  // No course/section information found
                }
            }
        }

虽然这段代码的执行需要相当长的时间(大约在10-30分钟之间),但是考虑到要解析的大量数据,这是意料之中的。感谢每个张贴答案的人,非常感谢。我希望这对其他可能有类似问题的人有所帮助。

谢谢,大卫

优化c#自定义Xml解析器类

嗯,显然加载XML文件有点慢(例如,因为它们很大或因为下载它们所需的时间),并且使用XDocument将加载并解析它们全部到内存中,即使您只使用其中的一小部分。三层递归地执行此操作将使整个过程非常缓慢,但最终它将结束(如预期的那样或通过OutOfMemoryException)。1

看看XmlReader类。它允许您按顺序读取XML文件,选择需要的内容,并在获得所需的所有信息时终止读取。然而,它的工作原理与XDocument有很大的不同,而且不那么直观。在那个MSDN页面上有一些例子,在Stackoverflow上也有。

作为旁注:当使用XmlReader时,请考虑在开始读取另一个XML文件之前读取并关闭阅读器。这将使应用程序的内存占用最小化。

1)考虑一下,例如,您正在阅读一个包含10年3季60课程的文件的文件,然后您的代码正在下载,解析,验证和处理10 * 3 * 60 = 1800个文件。它需要从(与你的本地电脑相比)缓慢的互联网下载它们。不要期望整个过程很快。

循环不是无限的,它只是变得非常非常慢。这是因为

的调用
XDocument hoursDoc = XDocument.Load(crsePath);

打开另一个XML文件并解析它。考虑到当所有信息都在内存中时,处理时间为25秒,因此为遇到的每个课程打开一个额外的文件会减慢处理速度,这并不奇怪。