使用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