让我说我有这个xml:
<categories>
<category text="Arts">
<category text="Design"/>
<category text="Visual Arts"/>
</category>
<category text="Business">
<category text="Business News"/>
<category text="Careers"/>
<category text="Investing"/>
</category>
<category text="Comedy"/>
</categories>
我想写一个LINQ查询,它将返回该类别,如果有的话,它是父类别。
例如,如果我在搜索“商业新闻”,我希望它返回包含以下内容的XElement:
<category text="Business">
<category text="Business News" />
</category>
如果我只搜索“商业”,我只想要
<category text="Business" />
到目前为止,我能做的最好的事情是使用LINQ来获取我正在搜索的元素,然后检查我找到的节点的父节点是否是根节点并相应地进行调整。还有更好的方法吗?
答案 0 :(得分:3)
简单的部分是获取元素的路径:
IEnumerable<XElement> elementsInPath =
doc.Element("categories")
.Descendants()
.Where(p => p.Attribute("text").Value == "Design")
.AncestorsAndSelf()
.InDocumentOrder()
.ToList();
InDocumentOrder()用于按照root,child,grandchild的顺序获取集合。 ToList()用于避免在下一步中产生任何不良影响。
现在,不太美丽的部分,也许可以以更优雅的方式完成:
var newdoc = new XDocument();
XContainer elem = newdoc;
foreach (var el in elementsInPath))
{
el.RemoveNodes();
elem.Add(el);
elem = elem.Elements().First();
}
就是这样。由于每个XElement保留其子节点,我们必须从路径中的每个节点中删除子节点,然后重建树。
答案 1 :(得分:1)
如果构建迭代器,问题会轻松得多:
public static IEnumerable<XElement> FindElements(XElement d, string test)
{
foreach (XElement e in d.Descendants()
.Where(p => p.Attribute("text").Value == test))
{
yield return e;
if (e.Parent != null)
{
yield return e.Parent;
}
}
}
在您使用Linq查询的任何地方使用它,例如:
List<XElement> elms = FindElement(d, "Visual Arts").ToList();
或
foreach (XElement elm in FindElements(d, "Visual Arts"))
{
...
}
修改强>
我现在看到上面代码提供的不是提问者所要求的。但是在我看来,提问者要求的内容有点奇怪,因为他想要返回的XElement
是一个全新的对象,而不是现有文档中的内容。
仍然,荣誉是为了服务。凝视我的作品,你们强大,绝望:
XElement result = doc.Descendants()
.Where(x => x.Attribute("text").Value == test)
.Select(
x => x.Parent != null && x.Parent.Attribute("text") != null
? new XElement(
x.Parent.Name,
new XAttribute("text", x.Parent.Attribute("text").Value),
new XElement(
x.Name,
new XAttribute("text", x.Attribute("text").Value)))
: new XElement(
x.Name,
new XAttribute("text", x.Attribute("text").Value)))
.FirstOrDefault();
答案 2 :(得分:1)
根据所述的输入和要求,这将满足您的需求:
public static class MyExtensions
{
public static string ParentAndSelf(this XElement self, XElement parent)
{
self.Elements().Remove();
if (parent != null && parent.Name.Equals(self.Name))
{
parent.Elements().Remove();
parent.Add(self);
return parent.ToString();
}
else
return self.ToString();
}
}
class Program
{
[STAThread]
static void Main()
{
string xml =
@"<categories>
<category text=""Arts"">
<category text=""Design""/>
<category text=""Visual Arts""/>
</category>
<category text=""Business"">
<category text=""Business News""/>
<category text=""Careers""/>
<category text=""Investing""/>
</category>
<category text=""Comedy""/>
</categories>";
XElement doc = XElement.Parse(xml);
PrintMatch(doc, "Business News");
PrintMatch(doc, "Business");
}
static void PrintMatch(XElement doc, string searchTerm)
{
var hit = (from category in doc
.DescendantsAndSelf("category")
where category.Attributes("text")
.FirstOrDefault()
.Value.Equals(searchTerm)
let parent = category.Parent
select category.ParentAndSelf(parent)).SingleOrDefault();
Console.WriteLine(hit);
Console.WriteLine();
}
}
答案 3 :(得分:0)
我没有测试过这个,但它应该是这样的:
XDocument xmlFile;
return from c in xmlFile.Descendants("category")
where c.Attribute("text").Value == "Business News"
select c.Parent ?? c;
??
运算符返回父XElement,如果那是null
'c'。
编辑:此解决方案会返回您想要的内容,但我不确定它是否是最好的,因为它变得非常复杂:
var cat = from c in doc.Descendants("category")
where c.Attribute("text").Value == "Business News"
let node = c.Parent ?? c
select c.Parent == null
? c // Parent null, just return child
: new XElement(
"category",
c.Parent.Attributes(), // Copy the attributes
c // Add single child
);
答案 4 :(得分:0)
var text = "Car";
var el = from category in x.Descendants("category")
from attribute in category.Attributes("text")
where attribute.Value.StartsWith(text)
select attribute.Parent.Parent;
Console.WriteLine(el.FirstOrDefault());
输出:
<category text="Business">...
即使没有这样的元素,或者没有这样的属性,这个也会起作用。