【问题标题】:Handling null values and missing columns in Linq-to-Xml处理 Linq-to-Xml 中的空值和缺失列
【发布时间】:2013-03-13 16:00:55
【问题描述】:

我有一个用户提供的数据表,其中包含以下信息:

| Name      | Alias        |
|---------------------------
| Batman    | Dark Knight  |
| Superman  | Man of Steel |
| Spiderman | null         |

我需要将此表转换为 Xml 以发送到存储过程并获取类似于以下内容的 Xml:

<Superheroes>
    <Superhero Name="Batman" Alias="Dark Knight"/>
    <Superhero Name="Superman" Alias="Man of Steel"/>
    <Superhero Name="Spiderman"/>
</Superheroes>

我想使用 Linq-to-Xml(用于学习目的)来解决这个问题。到目前为止,这是我所拥有的:

XDocument doc;

doc = new XDocument(
        new XElement("Superheroes",
        from row in table.AsEnumerable()
            select new XElement("Superhero",
                new XAttribute("Name", row.Field<string>("Name")),
                new XAttribute("Alias", row.Field<string>("Alias"))
)));

问题出在蜘蛛侠那一排。上面的查询因为 null 而不起作用。

问题

  1. 如何处理 Linq-to-Xml 中的空蜘蛛侠行?

  2. 如果用户忘记添加别名行,我需要能够处理该位置。在这种情况下,别名应该是 (a) 根本不在 Xml 中或 (b) 具有空值

【问题讨论】:

    标签: c# .net linq linq-to-xml


    【解决方案1】:

    您可以为此编写一个辅助函数,该函数将返回XAttributenull。 Linq to XML 将忽略null

    private XAttribute GetAlias(DataRow row)
    {
        if(row.Field<string>("Alias") != null)
        {
            return new XAttribute("Alias", row.Field<string>("Alias"));
        }
        return null;
    }
    

    然后像这样使用它。

    doc = new XDocument(
        new XElement("Superheroes",
        from row in table.AsEnumerable()
            select new XElement("Superhero",
                new XAttribute("Name", row.Field<string>("Name")),
                GetAlias(row)
    )));
    

    如果缺少 Alias 列,这也应该有效,因为如果缺少该列,Field&lt;T&gt;(string) 扩展方法应该返回 null

    或者,如果您只想在缺少列或值为null 时让属性具有空字符串,您可以执行以下操作。

    doc = new XDocument(
        new XElement("Superheroes",
        from row in table.AsEnumerable()
            select new XElement("Superhero",
                new XAttribute("Name", row.Field<string>("Name")),
                new XAttribute("Alias", row.Field<string>("Alias") ?? string.Empty)
    )));
    

    【讨论】:

    • 您的属性缺失解决方案效果很好。不幸的是,如果列丢失,Field(string) 会引发错误。
    • 在这种情况下,您只需要在辅助函数中添加对列是否存在的检查。
    【解决方案2】:

    如果属性名称应该与列标题相同,还有一个解决方案。您只能添加那些不具有空值的属性:

    doc = new XDocument(
             new XElement("Superheroes",
                from row in table.AsEnumerable()
                select new XElement("Superhero",
                    from column in table.Columns.Cast<DataColumn>()
                    let value = row.Field<string>(column)
                    where value != null // just filter out nulls
                    select new XAttribute(column.Caption, value)
            )));
    

    顺便说一句,蜘蛛侠不如蝙蝠侠或超人好


    实际上您可以创建以下扩展,它将根据表名和列名从任何 DataTable 构建 xml(它需要 System.Data.Entity.Design 程序集中的 PluralizationService):

    public static class Extensions
    {
        public static XDocument ToXml(this DataTable table)
        {
            if (String.IsNullOrEmpty(table.TableName))
               throw new ArgumentException("Table name is required");
    
            var pluralizationService = PluralizationService
                    .CreateService(new CultureInfo("en-US"));
            string elementName = pluralizationService.Singularize(table.TableName);
    
            return new XDocument(
                new XElement(table.TableName,
                    from row in table.AsEnumerable()
                    select new XElement(elementName,
                        from column in table.Columns.Cast<DataColumn>()
                        let value = row.Field<string>(column)
                        where value != null
                        select new XAttribute(column.Caption, value)
                        )
                    )
                );
        }
    }
    

    用法很简单:

    DataTable table = new DataTable("Superheroes"); // name is required
    table.Columns.Add("Name");
    table.Columns.Add("Alias");
    table.Rows.Add("Batman", "Dark Knight");
    table.Rows.Add("Superman", "Man Of Steel");
    table.Rows.Add("Spiderman", null);
    var doc = table.ToXml();
    

    结果还不错

    <Superheroes>
      <Superhero Name="Batman" Alias="Dark Knight" />
      <Superhero Name="Superman" Alias="Man Of Steel" />
      <Superhero Name="Spiderman" />
    </Superheroes>
    

    【讨论】:

    • 您能解释一下 PluralizationService 的情况吗?为什么不能只使用表名?
    • @JLo 当然。 PluralizationService 提供了构造单词的复数和单数形式的方法。 EF 使用它从复数表名中构造实体名。这就是我在这个解决方案中使用它的原因 - 表有复数名称Superheroes。 PluralizationService 以单数形式为行创建元素名称,即Superhero
    【解决方案3】:

    您可以使用内联 if 语句(使用 ?: operator)扩展您的查询,并检查当前行中是否设置了 Alias

    doc = new XDocument(
            new XElement("Superheroes",
            from row in table.AsEnumerable()
            select new XElement("Superhero",
                new XAttribute("Name", row.Field<string>("Name")),
                (row.Field<string>("Alias") != null ? new XAttribute("Alias", row.Field<string>("Alias")) : null))
    ));
    

    返回 null 根本不会更改生成的 XML,因此 doc 包含您希望它包含的内容:

    <Superheroes>
      <Superhero Name="Batman" Alias="Dark Knight" />
      <Superhero Name="Superman" Alias="Man of Steel" />
      <Superhero Name="Spiderman" />
    </Superheroes>
    

    【讨论】:

      猜你喜欢
      • 2011-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 2012-07-01
      相关资源
      最近更新 更多