您需要进行关注点分离:将您的问题分成可以独立使用的较小问题。这样可以更轻松地重用您的代码来解决其他问题,更轻松地测试您的代码,在不破坏代码的情况下进行更改。
您的问题包含两个子问题:
- 如何将 XML 数据转换为 Person 序列
- 如何从 Persons 序列中提取具有特定名称的 Persons
- 如何检测 Person 序列是否为空?
我不经常将 XML 转换为 IEnumerable,但this answer helped me.
class Person
{
public string Name {get; set;}
... // other properties
}
如果在你的 XML 中一个 Person 真的只有一个 Name,你不必为它定义一个特殊的类。但话又说回来:你确定一个人只不过是一个名字吗?那不应该是名字的集合吗?
好的,所以我们有一个 Person 类(或一个字符串),我们需要一个函数来将 XML 转换为 Person 序列:
把它写成一个扩展方法,这样我们就可以让它看起来像 LINQ。见extension methods demystified。签名中的单词this 使其成为扩展方法。它允许您像使用字符串类本身的方法一样使用该方法
public static IEnumerable<Person> ToPersons(this String xml)
{
return XElement.Parse(xml)
.Elements("Person")
.Select(xmlElement => new Person
{
Name = xmlElement.Element("Name").Value,
// if needed, fill other properties, for instance:
Id = Int32.Parse(xmlElement.Element("Id").Value),
});
}
用法:
string xmlTxt = ...
IEnumerable<Person> persons = xmlTxt.ToPersons();
如果您没有带有 xml 的字符串,而是一个 XmlReader,请考虑:
public static IEnumerable<Person> ToPersons(this XmlReader xmlReader)
{
while(xmlReader.Read())
{
// TODO: read one Person from xmlReader
Person person = new Person() {...} // you know better how to do this than I
yield return person
}
现在我们已经将 xml 转换与 Person 处理分开了,剩下的就很简单了:
要求:
给定一个 Person 的名称,以及一些表示 Person 序列的 XmlText,告诉我具有此名称的 Person 是否在此序列中
string personName = ...
string xmlText = ... // or Use the XmlReader
bool personAvailable = xmlText.ToPersons()
.Where(person => person.Name == personName)
.Any();
用词:将 xmlText 转换为 Persons 序列。从这个序列中,只保留那些Name 等于personName 的人。如果剩余序列中还有任何元素,则返回 true。
如果您使用特殊字符,或者想忽略大小写。考虑:
IEqualityComparer<strong> personNameComparer = StringComparer.CurrentCultureIgnoreCase;
bool personAvailable = xmlText.ToPersons()
.Where(person => personNameComparer.Equals(person.Name, personName))
.Any();
好消息是,因为您分离了您的关注点,您还可以将ToPersons 用于其他功能:
要求:给我所有 1960 年之前出生的纽约市居民
var oldPersons = xmlText.ToPersons
.Where(person => person.Location == "New York City" && person.Birthday.Year < 1960);
或者,如果您从 Json 文件、CSV 文件或数据库中获取您的 Persons,您仍然可以获取所有 1960 年之前出生的纽约市居民,而只需编写一个新的 ToPersons 方法。