如何用LINQ读取XML后同时验证构建日志的多个关联条件?
问题背景与需求
我负责的项目需要读取XML中各构建区域的预期构建结果,与构建日志对比以判断构建是否通过。目前通过LINQ读取XML时,每个条件被拆分为单独条目,但循环检查构建成功状态时,需要同时验证同一日志行的多个关联条件(例如ParseISLogStats对应的BuildProject和BaseBuildArea必须匹配同一条日志),否则会匹配到错误的日志行。此外,部分场景存在两个成功条件,需要实现可同时检查这类多条件的方法。
示例数据
XML预期结果示例
<?xml version="1.0" encoding="UTF-8"?> <BuildVerification> <codeFreezeTime>19:00</codeFreezeTime> <!-- 省略其他结构 --> <build> <BuildMachine>Server01</BuildMachine> <Process> <ProcessName>ParseISLogStats</ProcessName> <Condition> <SuccessCriteria> <ConditionName>BuildProject</ConditionName> <ConditionValue>DInstallerBuild</ConditionValue> <StartTimeHeader>StartTime</StartTimeHeader> <!-- 其他条件字段 --> </SuccessCriteria> <SuccessCriteria> <Field1>Status</Field1> <Comparison1>==</Comparison1> <Value1>Success</Value1> <!-- 其他条件字段 --> </SuccessCriteria> </Condition> </Process> </build> </BuildVerification>
构建日志示例
StartTime EndTime Day Elapsed sec Status BuildProj ... 04/16/24 21:57:31 04/16/24 22:02:46 Tue 00:05:15 315 Success DInstallerBuild ...
现有核心代码
ReadXml方法
void ReadXml() { int i_results = 0; string finalPathXml = Path.GetFullPath(Path.Combine(newPath, @"BuildVerificationBuildAttributes_t.xml")); XDocument xmlDoc1 = XDocument.Load(finalPathXml); var results = xmlDoc1.Descendants("build") .SelectMany(b => b.Descendants("Process") .SelectMany(p => p.Descendants("Condition") .SelectMany(c =>c.Descendants("SuccessCriteria") .Select(sc => new { buildMach = (string)b.Element("BuildMachine"), p1 = (string)p.Element("ProcessName"), // 省略其他字段映射 startTimeHeader = (string)sc.Element("StartTimeHeader"), cName = (string)sc.Element("ConditionName"), cValue = (string)sc.Element("ConditionValue"), field1 = (string)sc.Element("Field1"), comparison1 = (string)sc.Element("Comparison1"), val = (string)sc.Element("Value1") })))).ToList(); foreach (var cur in results) { i_results += ParseLogFile(codeFreezeTime, cur.startTimeHeader, cur.buildMach, cur.p1, cur.cName, cur.cValue, cur.f1, cur.c1, cur.v1); } }
ParseLogFile方法
int ParseLogFile(string codeFreezeTime, string startTimeHeader, string serverName, string logName, string cName, string cValue, string field1, string comparison1, string val) { int i_results = 0; DataTable dataTable = ReadDataTable(finalPath); foreach (DataRow row in dataTable.Rows) { if (Regex.IsMatch((row[startTimeHeader]).ToString(), @"^\d+")) { // 日期检查与单个条件验证逻辑 // ... } } return i_results; }
解决方案
1. 重构XML数据读取逻辑:按关联条件分组
原来的SelectMany会把所有SuccessCriteria拆分为独立条目,导致无法关联同一日志行的多个条件。现在需要按Process或Condition分组,将同一组的所有条件收集为一个集合:
void ReadXml() { int i_results = 0; string finalPathXml = Path.GetFullPath(Path.Combine(newPath, @"BuildVerificationBuildAttributes_t.xml")); XDocument xmlDoc1 = XDocument.Load(finalPathXml); // 按build -> Process分组,收集同一Process下的所有关联条件 var groupedResults = xmlDoc1.Descendants("build") .Select(b => new { BuildMachine = (string)b.Element("BuildMachine"), Processes = b.Descendants("Process") .Select(p => new { ProcessName = (string)p.Element("ProcessName"), // 收集当前Process下的所有SuccessCriteria条件 SuccessConditions = p.Descendants("Condition") .SelectMany(c => c.Descendants("SuccessCriteria")) .Select(sc => new { StartTimeHeader = (string)sc.Element("StartTimeHeader"), ConditionName = (string)sc.Element("ConditionName"), ConditionValue = (string)sc.Element("ConditionValue"), Field = (string)sc.Element("Field1"), Comparison = (string)sc.Element("Comparison1"), Value = (string)sc.Element("Value1") }) .ToList(), // 可选:读取Condition的逻辑类型(and/or) LogicType = (string)p.Element("Condition")?.Attribute("logic") ?? "and" }) .ToList() }) .ToList(); foreach (var build in groupedResults) { foreach (var process in build.Processes) { // 传入一组关联条件,一次性验证同一日志行 i_results += ParseLogFileWithMultipleConditions(codeFreezeTime, build.BuildMachine, process.ProcessName, process.SuccessConditions, process.LogicType); } } }
2. 实现多条件验证方法
新增方法接收一组条件,遍历日志行时验证当前行是否满足所有(或任一)关联条件,避免匹配错误行:
int ParseLogFileWithMultipleConditions(string codeFreezeTime, string serverName, string logName, List<dynamic> successConditions, string logicType) { int i_results = 0; DataTable dataTable = ReadDataTable(finalPath); if (!successConditions.Any()) return i_results; foreach (DataRow row in dataTable.Rows) { // 跳过无效时间行 if (!Regex.IsMatch(row[successConditions[0].StartTimeHeader].ToString(), @"^\d+")) continue; // 验证通用条件:日期、服务器名、日志名等 if (!ValidateGeneralConditions(row, codeFreezeTime, serverName, logName)) continue; // 根据逻辑类型验证条件 bool isConditionsMet = false; if (logicType.Equals("and", StringComparison.OrdinalIgnoreCase)) { // 需满足所有关联条件 isConditionsMet = successConditions.All(cond => ValidateSingleCondition(row, cond)); } else if (logicType.Equals("or", StringComparison.OrdinalIgnoreCase)) { // 需满足任一成功条件 isConditionsMet = successConditions.Any(cond => ValidateSingleCondition(row, cond)); } if (isConditionsMet) { i_results++; // 找到匹配行后可break,避免重复计数(根据业务需求调整) break; } } return i_results; } // 抽离单个条件验证逻辑,复用代码 bool ValidateSingleCondition(DataRow row, dynamic condition) { // 实现单个条件的验证逻辑,示例: if (!string.IsNullOrEmpty(condition.ConditionName)) { if (condition.ConditionName == "BuildProject" && row["BuildProj"].ToString() == condition.ConditionValue) return true; // 其他ConditionName类型的验证 } if (!string.IsNullOrEmpty(condition.Field)) { string rowValue = row[condition.Field].ToString(); switch (condition.Comparison) { case "==": return rowValue == condition.Value; case ">": if (int.TryParse(rowValue, out int rowInt) && int.TryParse(condition.Value, out int condInt)) return rowInt > condInt; break; // 其他比较逻辑 } } return false; } // 抽离通用条件验证逻辑 bool ValidateGeneralConditions(DataRow row, string codeFreezeTime, string serverName, string logName) { // 实现日期检查、服务器名匹配、日志名匹配等通用逻辑 // 示例:验证构建时间晚于codeFreezeTime // ... return true; }
3. 可选:扩展XML结构支持逻辑类型
如果需要区分条件是“同时满足”还是“任一满足”,可以给Condition节点添加logic属性:
<Condition logic="and"> <SuccessCriteria>...</SuccessCriteria> <SuccessCriteria>...</SuccessCriteria> </Condition>
读取时将该属性纳入分组结果,验证时根据属性选择All或Any逻辑。
内容的提问来源于stack exchange,提问作者Michele
相关产品推荐
相关产品推荐

