C#生成XML时如何避免添加无Url的Photo节点
问题:LINQ to XML生成产品XML时过滤空Photo节点
我使用C#的LINQ to XML从DataTable生成产品XML,当前代码会在Photos节点下生成不含Url子节点的空Photo元素,示例如下:
<Photos> <Photo> <Url>https://some.pl/Url> <IsPrimary>true</IsPrimary> </Photo> <Photo> <IsPrimary>false</IsPrimary> </Photo> <Photo> <IsPrimary>false</IsPrimary> </Photo> <!-- 更多空Photo节点 --> </Photos>
我需要只保留包含有效Url的Photo元素,期望输出:
<Photos> <Photo> <Url>https://some.pl/Url> <IsPrimary>true</IsPrimary> </Photo> <Photo> <Url>https://some.pl</Url> <IsPrimary>false</IsPrimary> </Photo> </Photos>
原生成代码如下:
//// Group the rows by Department (all 123A in your question). var departments = dt.AsEnumerable().GroupBy(r => r.Field<string>("ID")); // Generate the root element var root = new XElement("Catalog", departments.Select( d => new XElement("Product", new XElement("Id", d.Key), d.Select(r => new XElement("LastMod", r.Field<string>("Update_date"))), d.Select(r => new XElement("StructuredId", r.Field<string>("EAN"))), new XElement("Descriptions", d.Select(r => new XElement("Name", r.Field<string>("Name"))), d.Select(r => new XElement("Description", r.Field<string>("Description"))) ), new XElement("Prices", d.Select(r => new XElement("Currency", "EUR")), d.Select(r => new XElement("Price", r.Field<double>("Price_netto_EUR"))) ), new XElement("Warranty", d.Select(r => new XElement("Type", "Year")), d.Select(r => new XElement("Duration", r.Field<string>("Guarantee").Replace(" years", ""))) ), new XElement("Categories", new XElement("Category", d.Select(r => new XElement("Name", r.Field<string>("Category"))) )), new XElement("Photos", new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("ImageURL1"))), new XElement("IsPrimary", "true") ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("ImageURL2"))), new XElement("IsPrimary", "false") ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("ImageURL3"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("ImageURL4"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("ImageURL5"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("FileURL1"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("FileURL2"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("FileURL3"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("FileURL4"))), d.Select(r => new XElement("IsPrimary", "false")) ), new XElement("Photo", d.Select(r => new XElement("Url", r.Field<string>("FileURL5"))), d.Select(r => new XElement("IsPrimary", "false")) ) ), new XElement("Availability", d.Select(r => new XElement("Available", r.Field<string>("Available"))), d.Select(r => new XElement("Quantity", r.Field<string>("STOCK"))), d.Select(r => new XElement("ShippingDelay", r.Field<string>("LEADTIME_DAYS"))) ), new XElement("Attributes", new XElement("Attribute", d.Select(r => new XElement("AttributeName", "Style")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Style"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "PrimaryColor")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Primary_color"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "UpholysteryMaterial")), d.Select(r => new XElement("AttributeValue", r.Field<string>("UpholysteryMaterial"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "WeightCapacity")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Weight_capacity"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "SeatAreaWidth")), d.Select(r => new XElement("AttributeValue", r.Field<string>("SeatAreaWidth"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "SeatAreaDepth")), d.Select(r => new XElement("AttributeValue", r.Field<string>("SeatAreaDepth"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "SeatCapacity")), d.Select(r => new XElement("AttributeValue", r.Field<string>("SeatCapacity"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "SleepFunction")), d.Select(r => new XElement("AttributeValue", r.Field<string>("SleepFunction"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "PackagesNumbers")), d.Select(r => new XElement("AttributeValue", r.Field<double>("Packages_Numbers"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "TotalWeight")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Total_Weight"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "AdultAssemblyRequired")), d.Select(r => new XElement("AttributeValue", r.Field<string>("AdultAssemblyRequired"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "AssemblyLevel")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Assembly_Level"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "Backtype")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Backtype"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "FrameMaterial")), d.Select(r => new XElement("AttributeValue", r.Field<string>("FrameMaterial"))) ), new XElement("Attribute", d.Select(r => new XElement("AttributeName", "Framewoodtype")), d.Select(r => new XElement("AttributeValue", r.Field<string>("Framewoodtype"))) ) ), new XElement("VolumeDimensions", d.Select(r => new XElement("Width", r.Field<string>("Width"))), d.Select(r => new XElement("Height", r.Field<string>("Height"))), d.Select(r => new XElement("Length", r.Field<string>("Length"))), d.Select(r => new XElement("Depth", r.Field<string>("Depth"))) ) ) ) );
解决方案:过滤空URL的Photo节点
问题根源是原代码逐个创建Photo元素,不管对应URL字段是否为空。修改思路是:先定义所有图片URL字段及其对应的IsPrimary状态,过滤掉空URL的项后再生成Photo元素。
修改后的Photos节点代码如下:
new XElement("Photos", // 定义所有图片URL字段和对应的IsPrimary状态 new List<(string FieldName, bool IsPrimary)> { ("ImageURL1", true), ("ImageURL2", false), ("ImageURL3", false), ("ImageURL4", false), ("ImageURL5", false), ("FileURL1", false), ("FileURL2", false), ("FileURL3", false), ("FileURL4", false), ("FileURL5", false) } .Select(field => { // 分组后同一ID的行字段值一致,取第一个行的字段值 var url = d.First().Field<string>(field.FieldName); // 过滤空URL if (string.IsNullOrWhiteSpace(url)) return null; return new XElement("Photo", new XElement("Url", url), new XElement("IsPrimary", field.IsPrimary.ToString().ToLower()) ); }) // 移除null项,避免生成空Photo元素 .Where(photo => photo != null) )
关键说明
- 用元组列表统一管理所有图片字段和
IsPrimary状态,代码更简洁易维护。 - 分组后同一ID的行数据一致,直接取
First()获取URL值即可,无需遍历整组。 - 通过
string.IsNullOrWhiteSpace判断URL有效性,为空则返回null。 - 最后用
Where(photo => photo != null)过滤掉空项,确保只生成含有效URL的Photo元素。
完整修改后的代码只需要替换原代码中Photos节点的部分即可。
内容的提问来源于stack exchange,提问作者RemuRemue
相关产品推荐
相关产品推荐

