C# 动态多节点分组XDocument生成指定嵌套XML方案咨询
DataTable按前4列分组生成指定嵌套XML实现方案
核心思路
不需要依赖动态GroupBy的NTuple封装、也不需要用Except做列差计算,直接基于列索引确定分组列,遍历分组结果逐层构造XElement节点即可,完全兼容动态列场景,仅硬编码3个固定节点名。
实现步骤
- 提取分组列:取DataTable前4列作为分组维度,不需要提前知道列名
- 执行分组:以前4列的行值为键做分组,不需要自定义NTuple类
- 构造XML结构:
- 先创建
<ROOT>根节点 - 每个分组对应一个
<ROW>节点,节点下先挂载前4列的同名节点、对应值取分组键的值 - 每个分组内的明细行对应一个
<BID_INTERVALS>节点,节点下固定挂载<SEGMENT>、<Price>、<QTY>三个子节点,值取对应行的固定列值
- 先创建
可直接运行的代码
首先引入所需命名空间:
using System.Data; using System.Linq; using System.Xml.Linq;
核心转换逻辑:
// 初始化XML根节点 XElement root = new XElement("ROOT"); // 取前4列作为分组列,定义固定列名集合(不区分大小写匹配,避免列名大小写不统一导致的报错) List<DataColumn> groupCols = dataTable.Columns.Cast<DataColumn>().Take(4).ToList(); HashSet<string> fixedColNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "SEGMENT", "PRICE", "QTY" }; // 按前4列值分组 var groups = dataTable.AsEnumerable() .GroupBy(r => new { ColValues = groupCols.Select(c => r[c]).ToArray() }); // 逐分组生成ROW节点 foreach (var g in groups) { XElement rowNode = new XElement("ROW"); // 挂载前4列的节点 for (int i = 0; i < groupCols.Count; i++) { string colName = groupCols[i].ColumnName; string colVal = g.Key.ColValues[i] == DBNull.Value ? "" : g.Key.ColValues[i].ToString(); rowNode.Add(new XElement(colName, colVal)); } // 生成分组下所有BID_INTERVALS节点 foreach (DataRow detail in g) { XElement bidNode = new XElement("BID_INTERVALS"); string segVal = detail["Segment"] == DBNull.Value ? "" : detail["Segment"].ToString(); string priceVal = detail["Price"] == DBNull.Value ? "" : detail["Price"].ToString(); string qtyVal = detail["Qty"] == DBNull.Value ? "" : detail["Qty"].ToString(); bidNode.Add(new XElement("SEGMENT", segVal)); bidNode.Add(new XElement("Price", priceVal)); bidNode.Add(new XElement("QTY", qtyVal)); rowNode.Add(bidNode); } root.Add(rowNode); } // 最终输出目标XDocument XDocument resultXml = new XDocument(root);
之前方案失效原因
之前使用NTuple做分组后调用CopyToDataTable()转回DataTable的逻辑,本质是把分组后的层级数据又拍平成了二维表结构,完全丢失了分组和明细的嵌套关系,自然无法生成符合要求的嵌套XML。
适配说明
- 代码默认处理了DBNull空值,不会因为数据库空值抛出异常
- 固定列名匹配默认不区分大小写,如果业务要求严格匹配大小写,移除
HashSet初始化时的StringComparer.OrdinalIgnoreCase参数即可 - 除3个固定节点外,所有列名均从DataTable动态读取,不需要硬编码
内容的提问来源于stack exchange,提问作者siddhant Kumar
相关产品推荐
相关产品推荐

