使用Linq,如何将Xml解析为只接受构造函数中的参数的C#对象?

使用Linq,如何将Xml解析为只接受构造函数中的参数的C#对象?

问题描述:

假设我有一些这样的XML:使用Linq,如何将Xml解析为只接受构造函数中的参数的C#对象?

<User> 
    <Name>X</Name> 
    <Gender>Y</Gender> 
    <ImageUrl>Z</ImageUrl> 
</User> 

,我有一个名为User类。

public class User 
{ 
    public User(string name, string gender, string imageUrl) 
    { 
     Name = name; 
     Gender = gender; 
     ImageUrl = imageUrl; 
    } 
    public string Name { get; } 
    public string Gender { get; } 
    public string ImageUrl { get; } 
} 

只接受一个构造public User(string name, string gender, string ImageUrl),不允许设置的属性,是什么来解析这个XML到使用LINQ和C#这些对象的最佳方式?

粗略地说,可以创建匿名对象,然后迭代它们以创建所需的对象。有没有更有效的方法来做到这一点?

根本不需要任何匿名对象。使用LINQ to XML你可以简单地选择用户从XML节点,并通过与从XML节点选择的值调用构造函数创建实际User类实例:

// xml contains XML string, like in your sample 
var document = XDocument.Parse(xml); 
var users = document.Descendants("User") 
    .Select(u => new User(
     u.Element("Name").Value, 
     u.Element("Gender").Value, 
     u.Element("ImageUrl").Value 
    )); 

如果你想这是可重复使用的大多数类,方法这里的东西我过去曾经使用过。

请记住,这使用反射,所以它不像jimmy_keen的答案那么快。既然你提到的“高效”,这会不会跑一样快,他的答案 - 以它为它的价值:

下面的代码:

ConstructorInfo GetBestConstructor(XElement elm, Type itemType) 
    { 
     var elements = elm.Elements(); 

     // Get a constructor with the most parameters that 
     // are provided by xml elements. 
     var ctor = (
      from c in itemType.GetConstructors() 
      let p = c.GetParameters() 
      where 
       p.Length > 0 && 
       p.Count(parm => elements.Any(e => e.Name.LocalName.Equals(parm.Name, StringComparison.InvariantCultureIgnoreCase))) == p.Length 
      select c 
     ) 
      // Put the constructor with the most matching parameters 
      // at the top of the list 
     .OrderByDescending(c => c.GetParameters().Length) 
     .FirstOrDefault(); 

     return ctor; 
    } 

    TType Construct<TType>(XElement elm) 
    { 
     var ctor = GetBestConstructor(elm, typeof(TType)); 
     if (ctor != null) 
     { 
      // We found a valid contructor! 
      List<object> parameters = new List<object>(); 

      // Build a list of parameters, deserializing as we go. 
      foreach (var p in ctor.GetParameters()) 
      { 
       var item = elm.Elements().FirstOrDefault(e => e.Name.LocalName.Equals(p.Name, StringComparison.InvariantCultureIgnoreCase)); 
       if (item != null) 
       { 
        TypeConverter converter = TypeDescriptor.GetConverter(p.ParameterType); 
        if (converter != null && 
         converter.CanConvertFrom(typeof(string))) 
        { 
         // Deserialize each parameter and add it. 
         var parameter = converter.ConvertFrom(item.Value); 
         parameters.Add(parameter); 
        } 
       } 
      } 

      // Create the object, using each parameter we've deserialized 
      // to pass to the constructor. 
      return (TType)ctor.Invoke(parameters.ToArray()); 
     } 
     return default(TType); 
    } 

    public class User 
    { 
     public User(string name, string gender, string imageUrl) 
     { 
      Name = name; 
      Gender = gender; 
      ImageUrl = imageUrl; 
     } 
     public string Name { get; protected set; } 
     public string Gender { get; protected set; } 
     public string ImageUrl { get; protected set; } 
    } 

    public IEnumerable<User> GetUsers() 
    { 
     XDocument doc = XDocument.Parse(@" 
<User> 
    <Name>X</Name> 
    <Gender>Y</Gender> 
    <ImageUrl>Z</ImageUrl> 
</User>"); 

     return doc 
      .Descendants("User") 
      .Select(u => Construct<User>(u)); 
    } 

    static public void Main() 
    { 
     var p = new Program(); 
     var users = p.GetUsers().ToArray(); 
    } 

可能是矫枉过正满足您的需求,但想到我无论如何,它都会把它扔出去。

-Doug