0

職場の同僚が、クエリを実行しようとしたときに非常に珍しい 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このFoo にはBar ノードがあり、このBar ノードの N 属性があります「教育」であり、属性教育を持つこのバーノードに、LINQ で指定した単語を含まない値を持つ V がある場合のみ

たとえば、教育を持つ Foo ノードのすべてのユーザー ID に「Some」という単語が含まれないようにする場合、2,4 という結果が得られます。 V アトリビュートに Some 文字列があり、Foo 番号 3 がある N アトリビュートに Education 値がある Bar ノードがない (非常に重要です。これが常に空の結果を取得する理由の 1 つだと考えているためです。私たちがすることはありません)。

アイデアをお持ちの LINQ to XML の専門家なら誰でも、これは XML の非常に珍しいシナリオですが、対処しなければならないことであり、この質問はここにいる多くの人々の関心を引くでしょう。

4

4 に答える 4

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がない場合の処理​​を行うためです。Vまた、単語に一致するようにパターンを変更することもできます(単語の一部ではありません)。


すべてのFooノードにUser要素がある場合は、ユーザーのnullチェックを削除できます。また、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");
于 2012-12-28T22:24:08.860 に答える
2

オプションを開いたままにしておくために、LINQ の代わりに XPath を使用するソリューションを次に示します。これには、ジョンの回答によるエラーチェックは含まれていませんが、すべて同じように機能します。

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
}
于 2012-12-28T21:47:11.157 に答える
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 クエリの完全なソースです。

main where は foo の要素に取り組んでいます。次に、要素 (具体的には "Bar" 要素とその属性) をチェックして、適用するルールを確認します。

ここでの重要な問題は、このタイプのクエリがどの程度保守可能かということです。このような linq クエリを維持できますか? LinqPad で作業してみてください -- これにより、これらのクエリの変更と開発が、あなた (または誰でも) にとってより簡単になると思います。


ユーザーIDのリストを取得するには(ジョンの答えとして)、追加するだけです

.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();
于 2012-12-28T21:13:48.753 に答える
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」要素を考慮しますが、「Education」の「N」属性と「Specific Text」の「V」属性を持つ「Bar」要素がある要素のみを考慮します (任意の述語を配置できます)。あなたはすぐそこに欲しい)。これらの選択された要素のそれぞれについて、「ユーザー」要素を引き出します (1 つあると仮定し解析して「ID」属性を返します。

投稿した XML の例では、これは 2 と 4 を返します。

于 2012-12-28T20:54:41.367 に答える