2012-10-29 28 views
1

我有一個嵌套的xml,就像這樣。使用linq查找嵌套列表中的最後一條記錄

< H> 

    <FP> 
     < Name="FP1"/> 

     <R> 
     < Name = "R1"/> 

      < O> 
      < Name="O1"/> 
      < /O> 
      < O> 
       < Name="O2"/> 
      < /O> 
    < /R>  
    < R> 
    < Name = "R2"/> 
     < O> 
    < Name="O4"/> 
      < /O> 
    < /R> 
< /FP> 

< FP> 
    < Name="FP2" /> 

    < R> 
      < Name = "R3"/> 

      < O> 
      < Name="O5"/> 
      < /O> 
      < O> 
      < Name="O6"/> 
      < /O> 
    < /R> 
< /FP> 

    < R> 
     < Name="R4"/> 
     < O> 
    < Name="O7"/> 
    < /O> 
     < O> 
      < Name="O8"/> 
     < /O> 
    < /R> 
< R> 
    < Name="R5"/> 
    <O> 
      < Name="O9"/> 
    < /O> 
    < /R> 

< /H> 

我使用的反序列化閱讀本XML。

這裏是我的反序列化類:

[XmlRoot("H")] 
public class ReplyH 
{ 
    [XmlElement("FP")] 
    public List<ReplyFP> FPs; 

    [XmlElement("R")] 
    public List<ReplyR> Rs; 

} 

public class ReplyFP 
{ 
    [XmlElement("Name")] 
    public string Name; 

    [XmlElement("R")] 
    public List<ReplyR> Rs = new List<ReplyR>(); 
} 


public class ReplyR 
{ 
    [XmlElement("Name")] 
    public string Name; 

    [XmlElement("O")] 
    public List<ReplyO> Os = new List<ReplyO>(); 
} 


public class ReplyO 
{ 
    [XmlElement("Name")] 
    public string Name; 
} 

我需要遍歷O標籤。

類似的foreach(在H.FP.R.O變種O)和foreach(VARØ在H.R.O)

1)。我需要找到最後<O>標籤在上面的示例中是 < O> <名稱= 「O9」/> </O>. 2). I also need to get the parent [R Name and FP Name if exists for a particular O`

我怎樣才能得到這個使用LINQ/lambda表達式?

感謝

+0

我能看看你的反串行化代碼?我想知道一旦數據被反序列化,數據的形式如何。 – Mike

回答

1

下面是使用Xml.Linq:更新又見competing answer for updated question

using System.Linq; 
using System.Xml.Linq; 
using System.Xml.XPath; 
using System; 

public class stuff 
{ 
    public static void Main(string[] args) 
    { 
     var doc = XDocument.Parse(xml); 

     var last = doc.Root 
      .XPathSelectElements("//H/FP/R/O | //H/R/O") 
      .Last(); 

     Console.WriteLine(last); 
     Console.WriteLine("Parent R has name '{0}'", last.Parent.Attribute("Name").Value); 

     var granddad = last.Parent.Parent; 
     if (granddad.Name == "FP") 
      Console.WriteLine("Parent FP has name '{0}'", granddad.Attribute("Name").Value); 
    } 

    const string xml = @"<H> 
     <FP Name=""FP1""> 
      <R Name=""R1""> 
       <O Name=""O1""/> 
       <O Name=""O2""/> 
      </R>  
      <R Name=""R2""> 
       <O Name=""O4""/> 
      </R> 
     </FP> 
     <FP Name=""FP2""> 
      <R Name=""R3""> 
       <O Name=""O5""/> 
       <O Name=""O6""/> 
      </R> 
     </FP> 
    <!-- 
     <R Name=""R4""> 
      <O Name=""O7""/> 
      <O Name=""O8""/> 
     </R> 
     <R Name=""R5""> 
      <O Name=""O9""/> 
     </R> 
    --> 
     </H>"; 
} 

輸出

<O Name="O6" /> 
Parent R has name 'R3' 
Parent FP has name 'FP2' 

如果取消註釋從XML最後一節:

<!-- 
    <R Name=""R4""> 
     <O Name=""O7""/> 
     <O Name=""O8""/> 
    </R> 
    <R Name=""R5""> 
     <O Name=""O9""/> 
    </R> 
--> 

輸出變爲:

<O Name="O9" /> 
Parent R has name 'R5' 
+0

我更新了反序列化類的問題。這只是一個巨大的XML和一個巨大的反序列化類的一部分。所以我現在不能改變使用LINQ到XML :(希望有一個出路 – user1186390

+0

@ user1186390我已經提供[另一個答案](http://stackoverflow.com/a/13131513/85371)到您更新的問題。 – sehe

+0

感謝很多的迴應。至於LINQ到對象回答你上面貼的,我確實有的< R>< O>和< FP>< R>< O>隨機順序所以< R>< O>小號不必然遵循< FP>標籤。所以這裏的解決方案可能無法正常工作。對於linq to xml,我在Win 8 metro樣式應用程序中實現了這個功能,看起來好像沒有System.xml.xpath。它會導致它失敗。 – user1186390

1

要更新的問題,使用LINQ到對象更新的答案。

該代碼是不那麼優雅,然後Linq到XML falvour,但它的作品。

注意

  1. 因爲FPS和R分別在不同的收藏在頂層,有沒有辦法知道自己的文件排序作爲源XML。我假設Rs在ReplyH之內跟隨FP,就像代碼顯示的那樣(以及示例XML似乎也表明了這一點)。
  2. 我不得不修復序列化類使用

    [XmlAttribute("Name")] public string Name; 
    

    它(意外?)在你的問題說:XmlElement("Name")

using System.Collections.Generic; 
using System.Xml.Serialization; 
using System.Linq; 
using System.IO; 
using System; 

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     ReplyH doc; 
     using (var reader = new StringReader(xml)) 
      doc = (ReplyH) new XmlSerializer(typeof(ReplyH)).Deserialize(reader); 

     var Rs = doc.FPs 
      .SelectMany(fp => fp.Rs.Select(r => new { Parent = fp   , R=r })) 
      .Concat ( doc.Rs.Select(r => new { Parent = (ReplyFP) null, R=r })); 

     var Os = Rs.SelectMany(r => r.R.Os.Select(o => new { Parent = r, O=o })); 

     var lastO = Os.Last(); 
     Console.WriteLine(lastO.O.Name); 

     if (lastO != null) 
     { 
      Console.WriteLine("Parent R has name '{0}'", lastO.Parent.R.Name); 

      ReplyFP granddad = lastO.Parent.Parent; 
      if (granddad != null) 
       Console.WriteLine("Parent FP has name '{0}'", granddad.Name); 
     } 
    } 

    const string xml = @"<H> 
     <FP Name=""FP1""> 
      <R Name=""R1""> 
       <O Name=""O1""/> 
       <O Name=""O2""/> 
      </R>  
      <R Name=""R2""> 
       <O Name=""O4""/> 
      </R> 
     </FP> 
     <FP Name=""FP2""> 
      <R Name=""R3""> 
       <O Name=""O5""/> 
       <O Name=""O6""/> 
      </R> 
     </FP> 
    <!-- 
     <R Name=""R4""> 
      <O Name=""O7""/> 
      <O Name=""O8""/> 
     </R> 
     <R Name=""R5""> 
      <O Name=""O9""/> 
     </R> 
    --> 
     </H>"; 
} 

[XmlRoot("H")] public class ReplyH { 
    [XmlElement("FP")] public List<ReplyFP> FPs = new List<ReplyFP>(); 
    [XmlElement("R")] public List<ReplyR> Rs = new List<ReplyR>(); 
} 

public class ReplyFP { 
    [XmlAttribute("Name")] public string Name; 
    [XmlElement("R")] public List<ReplyR> Rs = new List<ReplyR>(); 
} 

public class ReplyR { 
    [XmlAttribute("Name")] public string Name; 
    [XmlElement("O")] public List<ReplyO> Os = new List<ReplyO>(); 
} 

public class ReplyO { 
    [XmlAttribute("Name")] public string Name; 
} 

輸出:

O6 
Parent R has name 'R3' 
Parent FP has name 'FP2' 

或之後取消註釋XML的最後一塊:

O9 
Parent R has name 'R5' 
+1

@ user1186390這完全回答你的問題。如果您意識到應該保持順序,那麼無論如何您的XSD /序列化類都有問題。我認爲提出的問題是通過這個答案解決的(例子很明顯)。 – sehe

+0

謝謝你的幫助。如果沒有R,我將添加一個虛擬FP,並且這樣我仍然可以使用上面的solutoin來獲取父級和父級。 – user1186390

相關問題