如何通过LINQ将含嵌套statement的SP800-53 XML加载到DataTable
解决方案
问题分析
你的代码存在两个核心问题:
- 命名空间处理缺失:XML里的
family、statement等元素属于默认命名空间http://scap.nist.gov/schema/sp800-53/2.0,直接用Element("family")会找不到元素。 - 父级字段获取错误:嵌套的
statement的父节点还是statement,不是control,用i?.Parent?.Element("family")只能拿到最外层statement的父级数据,深层statement会获取到null。
修正后的代码
// 定义两个命名空间:controls前缀的feed命名空间,以及默认的sp800-53核心命名空间 XNamespace controlsNs = "http://scap.nist.gov/schema/sp800-53/feed/2.0"; XNamespace defaultNs = "http://scap.nist.gov/schema/sp800-53/2.0"; XDocument xdoc = XDocument.Load("80053.xml"); // 先遍历每个control节点,再提取该control下所有嵌套的statement var ccidata = xdoc.Descendants(controlsNs + "control") .SelectMany(control => control.Descendants(defaultNs + "statement") .Select(statement => new { // 从当前control节点获取父级字段 Family = control.Element(defaultNs + "family")?.Value, ControlNumber = control.Element(defaultNs + "number")?.Value, Title = control.Element(defaultNs + "title")?.Value, // 从当前statement节点获取自身字段 StatementNumber = statement.Element(defaultNs + "number")?.Value, Description = statement.Element(defaultNs + "description")?.Value }) ) // 过滤掉没有statement编号的空节点(比如最外层的空statement) .Where(item => !string.IsNullOrEmpty(item.StatementNumber)) .ToList(); // 将数据转换为DataTable DataTable dt = new DataTable(); dt.Columns.Add("Family", typeof(string)); dt.Columns.Add("ControlNumber", typeof(string)); dt.Columns.Add("Title", typeof(string)); dt.Columns.Add("StatementNumber", typeof(string)); dt.Columns.Add("Description", typeof(string)); foreach (var item in ccidata) { dt.Rows.Add( item.Family, item.ControlNumber, item.Title, item.StatementNumber, item.Description ); }
代码说明
- 命名空间处理:明确指定两个命名空间,确保能正确定位XML中的元素。
- SelectMany的使用:先遍历每个
control,再将每个control下的所有statement展开为扁平的集合,这样每个statement都能关联到所属的control字段。 - 过滤空节点:最外层的
statement没有number字段,通过Where过滤掉这类无效数据。 - DataTable转换:直接遍历集合,将每个对象的属性值添加到DataTable的行中。
内容的提问来源于stack exchange,提问作者Fisher8370
相关产品推荐
相关产品推荐

