一位正在工作的同事在尝试查询时遇到了一个非常不寻常的XML文件问题,在尝试帮助他之后,我和其他人都处于一个创造性的障碍......看看这个,它可能会让很多人感兴趣....
结构:
<Root>
<MainFoo>
<Foo>
<A bla="bla" />
<B bla1="blablabla" />
<C bla2="blabla" />
<Bar N="Education" V="Some Text" />
<Bar N="Other Node" V="Some other Text" />
<Bar N="Yet Other Node" V="Some other other Text" />
<Bar N="fourth Bar Node" V="Some other other otherText" />
<Bar N="UserID" V="1" />
</Foo>
<Foo>
<A bla="bla" />
<B bla1="blablabla" />
<C bla2="blabla" />
<Bar N="Education" V="Specific Text" />
<Bar N="Other Node" V="Some other Text" />
<Bar N="Yet Other Node" V="Some other other Text" />
<Bar N="fourth Bar Node" V="Some other other otherText" />
<Bar N="UserID" V="2" />
</Foo>
<Foo>
<A bla="bla" />
<B bla1="blablabla" />
<C bla2="blabla" /> <!--***No Bar node with N="Education" in this Foo Node, not a mistake! this might be part of the problem but this is the XML Structure and can't be changed***-->
<Bar N="Other Node" V="Some other Text" />
<Bar N="Yet Other Node" V="Some other other Text" />
<Bar N="fourth Bar Node" V="Some other other otherText" />
<Bar N="UserID" V="3" />
</Foo>
<Foo>
<A bla="bla" />
<B bla1="blablabla" />
<C bla2="blabla" />
<Bar N="Education" V="Specific Text" />
<Bar N="Other Node" V="Some other Text" />
<Bar N="Yet Other Node" V="Some other other Text" />
<Bar N="fourth Bar Node" V="Some other other otherText" />
<Bar N="UserID" V="4" />
</Foo>
</MainFoo>
<OtherMainFoo></OtherMainFoo>
<MoreMainFoo></MoreMainFoo>
</Root>
好的,现在针对手头的问题: 我们正在尝试使用LINQ to XML将每个用户节点的每个用户ID值 - 变为每个 Foo元素的字符串 IF < / em>此 Foo中有条形节点,此条形节点的N属性为“教育”且仅当此 >带有属性教育的bar节点的V值不包含我们在LINQ中指定的单词
例如,如果我们希望受教育的Foo节点的所有用户ID都不包含单词 “Some” ,我们将得到2,4的结果因为Foo第一个有一个具有N属性教育值的Bar节点,但它在V属性和Foo中有一些字符串3没有一个带有教育值的条形节点在它的N属性中(非常重要,因为我们我认为这是我们一直做空的结果的原因之一。)
这里有一个想法的LINQ to XML专家,对于XML来说这是一个非常不寻常的场景,但我们必须处理这个问题,而且这个问题会引起很多人的兴趣。
答案 0 :(得分:2)
TL;博士:
var hasEducation = contacts.Elements("MainFoo").Elements("Foo")
.Where(foo => foo.Elements("Bar")
.Any(bar => (bar.Attribute("N").Value == "Education") &&
(!bar.Attribute("V").Value.ToLower().Contains("some") )))
注意:我用LinqPad(http://www.linqpad.net/)对它进行了测试,并使用它并喜欢它。 LinqPad非常适合这些问题。以下是LinqPad查询的完整源代码,可以自行测试和播放。
主要工作在foo元素上。然后它会检查您要应用的规则的元素(特别是“Bar”元素及其属性)。
这里的关键问题是这种类型的查询是如何可维护的。你能保持像这样的linq查询吗?尝试使用LinqPad - 我相信它会使您(或任何人)更容易修改和开发这些查询。
要获取用户ID列表(作为John的答案),您只需添加
即可.Element("User").Attribute("ID").Value;
到上面的查询结束。
当然,这不包括John的性感错误检查。
XElement contacts = XElement.Parse (@"
<Root>
<MainFoo>
<Foo>
<A bla='bla' />
<B bla1='blablabla' />
<C bla2='blabla' />
<Bar N='Education' V='Some Text' />
<Bar N='Other Node' V='Some other Text' />
<Bar N='Yet Other Node' V='Some other other Text' />
<Bar N='fourth Bar Node' V='Some other other otherText' />
<User ID='1' />
</Foo>
<Foo>
<A bla='bla' />
<B bla1='blablabla' />
<C bla2='blabla' />
<Bar N='Education' V='Specific Text' />
<Bar N='Other Node' V='Some other Text' />
<Bar N='Yet Other Node' V='Some other other Text' />
<Bar N='fourth Bar Node' V='Some other other otherText' />
<User ID='2' />
</Foo>
<Foo>
<A bla='bla' />
<B bla1='blablabla' />
<C bla2='blabla' /> <!--***No Bar node with N='Education' in this Foo Node, not a mistake! this might be part of the problem but this is the XML Structure and can't be changed***-->
<Bar N='Other Node' V='Some other Text' />
<Bar N='Yet Other Node' V='Some other other Text' />
<Bar N='fourth Bar Node' V='Some other other otherText' />
<User ID='3' />
</Foo>
<Foo>
<A bla='bla' />
<B bla1='blablabla' />
<C bla2='blabla' />
<Bar N='Education' V='Specific Text' />
<Bar N='Other Node' V='Some other Text' />
<Bar N='Yet Other Node' V='Some other other Text' />
<Bar N='fourth Bar Node' V='Some other other otherText' />
<User ID='4' />
</Foo>
</MainFoo>
<OtherMainFoo></OtherMainFoo>
<MoreMainFoo></MoreMainFoo>
</Root>");
var hasEducation = contacts.Elements("MainFoo").Elements("Foo")
.Where(foo => foo.Elements("Bar")
.Any(bar => (bar.Attribute("N").Value == "Education") &&
(!bar.Attribute("V").Value.ToLower().Contains("some") )))
.Dump();
答案 1 :(得分:2)
为了保持您的选项开放,这是一个使用XPath而不是LINQ的解决方案。根据约翰的回答,这并不包括错误检查,但它的工作原理完全相同。
public static IEnumerable<string> GetIDs(XDocument doc, string negation)
{
//The following xpath string will select all Foo elements that contain a Bar child
// that has a N attribute with the value "Education" and also has a V attribute
// that does not contain the specified string.
string xPathString = String.Format("//Foo[(Bar/@N = 'Education') and (not(contains(Bar/@V, '{0}')))]", negation);
return doc.Root
.XPathSelectElements(xPathString) //Select the proper Foo elements
.Select(a => a.Element("User").Attribute("ID").Value); //Grab the User elements under the previous Foo elements and return their ID attribute value
}
答案 2 :(得分:2)
string text = "Some";
var query = from foo in xdoc.Descendants("Foo")
let user = foo.Element("User")
where user != null &&
foo.Elements("Bar")
.Any(bar => (string)bar.Attribute("N") == "Education" &&
!Regex.IsMatch((string)bar.Attribute("V"), text,
RegexOptions.IgnoreCase))
select (int)user.Attribute("ID");
// result: 2, 4
我使用正则表达式来搜索bar属性中的单词有两个原因 - 使搜索不区分大小写,并处理Bar
元素没有V
属性时的情况。您也可以更改模式以匹配单词(不是单词的一部分)。
如果所有Foo
个节点都有User
个元素,则可以删除用户的空检查。此外,如果Bar
元素始终包含V
属性,并且您不需要不区分大小写的搜索,则可以简化查询:
var query = from foo in xdoc.Descendants("Foo")
where foo.Elements("Bar")
.Any(bar => (string)bar.Attribute("N") == "Education" &&
!((string)bar.Attribute("V")).Contains(text))
select (int)foo.Element("User").Attribute("ID");
答案 3 :(得分:1)
以下似乎有效:
public static IEnumerable<int> QueryComplexXml()
{
var doc = XDocument.Parse(XML);
if (doc.Root == null)
{
throw new System.InvalidOperationException("No root");
}
var mainFoo = doc.Root.Element("MainFoo");
if (mainFoo == null)
{
throw new System.InvalidOperationException("No MainFoo");
}
var userIDs = from foo in mainFoo.Elements("Foo")
where
foo.Elements("Bar")
.Any(
bar =>
bar.Attribute("N").Value == "Education" &&
bar.Attribute("V").Value == "Specific Text")
let user = foo.Element("User")
where user != null
select int.Parse(user.Attribute("ID").Value);
return userIDs;
}
代码考虑所有“Foo”元素,但只考虑那些“Bar”元素具有“Education”的“N”属性和“Specific Text”的“V”属性的元素(你可以把你想要的任何谓词放在那里)。对于每个选定的元素,它会拉出“User”元素(假设是一个,并解析并返回“ID”属性。
在您发布的示例XML中,返回2和4。