简体   繁体   English

在Linq-to-Xml中处理空值和缺少列

[英]Handling null values and missing columns in Linq-to-Xml

I have a user-supplied DataTable with the following information: 我有一个用户提供的DataTable,其中包含以下信息:

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

I need to convert this table to Xml to send to a stored procedure and get Xml similar to the following: 我需要将此表转换为Xml以发送到存储过程并获取类似于以下内容的Xml:

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

I want to use Linq-to-Xml (for learning purposes) to solve this problem. 我想使用Linq-to-Xml(出于学习目的)解决此问题。 Here's what I have so far: 这是我到目前为止的内容:

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"))
)));

The problem is the Spiderman row. 问题是蜘蛛侠行。 The query above doesn't work because of the null. 上面的查询由于为null而无法正常工作。

Questions 问题

  1. How do I handle the null Spiderman row in Linq-to-Xml? 如何处理Linq-to-Xml中的空Spiderman行?

  2. If the user forgets to add the Alias row, I need to be able to handle that sitation. 如果用户忘记添加Alias行,则需要能够处理该请求。 In that case, the Alias should either be (a) not in the Xml at all or (b) have empty values 在这种情况下,别名应该是(a)完全不在Xml中,或者(b)具有空值

You can write a helper function for this that will either return the XAttribute or null . 您可以为此编写一个辅助函数,该函数将返回XAttributenull Linq to XML will just ignore the null . 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;
}

And use it like this. 并像这样使用它。

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

This should also work if the Alias column is missing because the Field<T>(string) extension method should return null if the column is missing. 如果缺少Alias列,这也应该起作用,因为如果缺少列,则Field<T>(string)扩展方法应返回null

Alternatively if you just want to have the attribute with an empty string when the column is missing or the value is null you can do the following. 或者,如果你只是想有一个空字符串的属性时,该列是丢失或值为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)
)));

另一种方法是像在第一个答案中那样使用三元运算符: 具有三元运算符的示例

One more solution if attribute names should be same as column captions. 如果属性名称应与列标题相同,则是另一种解决方案。 You can add only those attributes, which have not null values: 您只能添加没有空值的那些属性:

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)
        )));

BTW Spiderman is not that good as Batman or Superman BTW蜘蛛侠不如蝙蝠侠或超人好


Actually you can create following extension, which will build xml from any DataTable based on table name and column names (it requires PluralizationService from System.Data.Entity.Design assembly): 实际上,您可以创建以下扩展名,该扩展名将基于表名和列名从任何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)
                    )
                )
            );
    }
}

Usage is simple: 用法很简单:

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();

And result is pretty nice 结果很不错

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

You can extend your query with inline if statement (using ?: operator ) and check if Alias is set in current row: 您可以使用内联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))
));

Returning null does not change generated XML at all, so doc contains exactly what you want it to contain: 返回null根本不会更改生成的XML,因此doc包含您希望它包含的内容:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM