如何用StringBuilder和foreach循环构建不重复OrderId的XML
解决重复OrderId的XML生成问题
要实现相同OrderId只输出一次头部、循环输出对应明细的XML,核心思路是先按OrderId分组数据,再遍历每个分组生成订单节点——每个分组对应一个订单,先写一次头部,再遍历组内所有行生成明细。
步骤拆解与代码实现
1. 先准备数据结构(以DataReader为例)
如果你的sdrForEach是SqlDataReader,首先需要把数据读取到可分组的集合中(因为DataReader是只读向前的,无法重复遍历):
// 定义实体类存储每行数据 public class OrderDetail { public int OrderId { get; set; } public DateTime OrderDate { get; set; } public string CustomerName { get; set; } public int ItemId { get; set; } public string ItemName { get; set; } public int Quantity { get; set; } } // 从DataReader读取数据到列表 List<OrderDetail> orderList = new List<OrderDetail>(); while (sdrForEach.Read()) { orderList.Add(new OrderDetail { OrderId = sdrForEach.GetInt32(sdrForEach.GetOrdinal("OrderId")), OrderDate = sdrForEach.GetDateTime(sdrForEach.GetOrdinal("OrderDate")), CustomerName = sdrForEach.GetString(sdrForEach.GetOrdinal("CustomerName")), ItemId = sdrForEach.GetInt32(sdrForEach.GetOrdinal("ItemId")), ItemName = sdrForEach.GetString(sdrForEach.GetOrdinal("ItemName")), Quantity = sdrForEach.GetInt32(sdrForEach.GetOrdinal("Quantity")) }); }
2. 按OrderId分组(可选:包含头部字段确保一致性)
如果担心同一OrderId的头部信息(比如OrderDate、CustomerName)可能不一致,可以把这些字段也加入分组键,避免生成错误的XML:
// 按OrderId+头部信息分组,确保同一组的头部完全一致 var groupedOrders = orderList.GroupBy(o => new { o.OrderId, o.OrderDate, o.CustomerName });
3. 遍历分组生成XML
现在用foreach遍历每个分组,先写订单头部,再遍历组内明细:
StringBuilder xmlBuilder = new StringBuilder(); xmlBuilder.AppendLine("<Orders>"); foreach (var orderGroup in groupedOrders) { // 获取当前订单的头部信息(分组的Key) var header = orderGroup.Key; // 写入订单头部节点 xmlBuilder.AppendLine(" <Order>"); xmlBuilder.AppendLine($" <OrderId>{header.OrderId}</OrderId>"); xmlBuilder.AppendLine($" <OrderDate>{header.OrderDate:yyyy-MM-dd}</OrderDate>"); xmlBuilder.AppendLine($" <CustomerName>{EscapeXml(header.CustomerName)}</CustomerName>"); xmlBuilder.AppendLine(" <Items>"); // 遍历当前订单的所有明细行 foreach (var item in orderGroup) { xmlBuilder.AppendLine(" <Item>"); xmlBuilder.AppendLine($" <ItemId>{item.ItemId}</ItemId>"); xmlBuilder.AppendLine($" <ItemName>{EscapeXml(item.ItemName)}</ItemName>"); xmlBuilder.AppendLine($" <Quantity>{item.Quantity}</Quantity>"); xmlBuilder.AppendLine(" </Item>"); } // 闭合明细和订单节点 xmlBuilder.AppendLine(" </Items>"); xmlBuilder.AppendLine(" </Order>"); } xmlBuilder.AppendLine("</Orders>"); // 辅助方法:转义XML特殊字符(避免&、<、>等导致XML无效) private string EscapeXml(string input) { if (string.IsNullOrEmpty(input)) return input; return input.Replace("&", "&") .Replace("<", "<") .Replace(">", ">") .Replace("\"", """) .Replace("'", "'"); }
关键注意点
- XML特殊字符转义:一定要用
EscapeXml这类方法处理用户输入的文本,否则如果内容包含&、<等字符,会生成无效的XML。 - 分组的安全性:如果业务上同一OrderId的头部信息可能不一致,一定要把头部字段加入分组键,避免把不同头部的订单合并到一起。
- 数据读取的必要性:如果用的是DataReader,必须先把数据读到集合中再分组,因为DataReader无法回溯遍历。
示例输出(对应你的需求)
假设你的原始数据是:
| OrderId | OrderDate | CustomerName | ItemId | ItemName | Quantity |
|---|---|---|---|---|---|
| 101 | 2024-05-01 | John Doe | 1 | Laptop | 1 |
| 101 | 2024-05-01 | John Doe | 2 | Mouse | 2 |
| 102 | 2024-05-02 | Jane Smith | 3 | Keyboard | 1 |
生成的XML会是:
<Orders> <Order> <OrderId>101</OrderId> <OrderDate>2024-05-01</OrderDate> <CustomerName>John Doe</CustomerName> <Items> <Item> <ItemId>1</ItemId> <ItemName>Laptop</ItemName> <Quantity>1</Quantity> </Item> <Item> <ItemId>2</ItemId> <ItemName>Mouse</ItemName> <Quantity>2</Quantity> </Item> </Items> </Order> <Order> <OrderId>102</OrderId> <OrderDate>2024-05-02</OrderDate> <CustomerName>Jane Smith</CustomerName> <Items> <Item> <ItemId>3</ItemId> <ItemName>Keyboard</ItemName> <Quantity>1</Quantity> </Item> </Items> </Order> </Orders>
内容的提问来源于stack exchange,提问作者paygboy00
相关产品推荐
相关产品推荐

