2013-07-19 70 views
1

我是新來的LINQ to XML,以及目前與下面的XML工作這個XML:如何閱讀使用LINQ to XML

<invoices> 
    <invoice> 
    <order_id>85</order_id> 
    <time>02:52 PM</time> 
    <date>24-05-2013</date> 
    <order> 
     <item> 
     <Main> 
      <id>343</id> 
      <Qty>1</Qty> 
     </Main> 
     <Add /> 
     </item> 
     <item> 
     <Main> 
      <id>3</id> 
      <Qty>1</Qty> 
     </Main> 
     <Add> 
      <Extra id="1"> 
      <Qty>1</Qty> 
      <Desc>Regular</Desc> 
      </Extra> 
     </Add> 
     </item> 
    </order> 
    </invoice> 
    <invoice> 
    <order_id>88</order_id> 
    <time>03:10 PM</time> 
    <date>24-05-2013</date> 
    <order> 
     <item> 
     <Main> 
      <id>345</id> 
      <Qty>1</Qty> 
     </Main> 
     <Add /> 
     </item> 
     <item> 
     <Main> 
      <id>2</id> 
      <Qty>2</Qty> 
     </Main> 
     <Add> 
      <Extra id="1"> 
      <Qty>1</Qty> 
      <Desc>Regular</Desc> 
      </Extra> 
     </Add> 
     </item> 
    </order> 
    </invoice> 
</invoices> 

到目前爲止,我已經寫了下面的代碼:

void queryData(XDocument doc) 
{ 
     var data = from item in doc.Descendants("invoice") 
        select new 
        { 
         orderId = item.Element("order_id").Value, 
         orderDate = item.Element("date").Value, 
         orderTime = item.Element("time").Value 
        }; 
     foreach(var p in data) 
      Console.WriteLine(p.ToString()); 

     //... 

} 

我無法閱讀「訂單」標籤中的嵌套標籤。元素/標籤「Add」有時也有「Extra」no。的標籤/元素,有時不。

我無法訪問生成此xml的代碼,因此必須閱讀此模式。

到目前爲止,我已嘗試與分組合作,但我無法使用第二和第三級別的元素。

閱讀後我會將這些值保存到數據庫。

感謝,

+0

您試圖從XML中實際得到什麼?顯然你想要訂單ID,日期和時間......還有什麼? –

+0

所有標記值 – AliR

+0

'也是元素/標記「添加」有時具有「額外」沒有。的標籤/元素,有時不是。「是的,你需要使用多態性來切換主/添加標籤。下面的答案就是一個例子。特別是,你應該使用類似於你上面的'Select',但是當你在'Main'或者'Add'項之間轉換時,確保'Select'到* base *類,因爲'Select'需要返回值是一致的類型。 – sircodesalot

回答

1

讓我知道如果我被什麼東西我就走,我沒有得到一個機會來測試這一點,也不得不承擔一些將要複製的元素

var data = from item in doc.Descendants ("invoice") 
    select new { 
     orderId = item.Element ("order_id").Value , 
     orderDate = item.Element ("date").Value , 
     orderTime = item.Element ("time").Value , 
     items = 
      from order in item.Element ("order").Descendants ("item") 
      let main = order.Element ("Main") 
      let adds = order.Elements ("Add") 
      select new { 
       Main = new { 
        id = main.Element ("id").Value , 
        Qty = main.Element ("Qty").Value 
       } , 
       Add = 
       (from add in adds 
        let extras = add.Elements ("Extra") 
        select new { 
           Extra = (from extra in extras 
             select new { 
               extraId = extra.Attribute("id").Value, 
               Qty = extra.Element ("Qty").Value , 
               Desc = extra.Element ("Desc").Value 
              }).FirstOrDefault () 
          }).FirstOrDefault() 
      } 
}; 
+0

謝謝@Charles Keyser - mainDescents沒有Element成員。它有ElementAt。我認爲這不是一個好主意,因爲訂單可能會改變。 – AliR

+0

@AliRajput是的我看到了問題,這應該工作 – konkked

+0

該代碼確實編譯,但不在控制檯上打印主要的值(只是爲了檢查它是否存儲在數據正確。 – AliR

1

對於嵌套的元素,只保留.Element("name")去:

orderQuantities = item.Element("order").Elements("item") 
    .Select(orderItem => new { 
     id = orderItem.Element("Main").Element("id")), 
     qty = orderItem.Element("Main").Element("Qty")) 
    }).ToArray(), 

對於你不知道存在的元素,你可以寫一個輔助方法:

extraQty = GetExtra(item), 
其中 GetExtra會類似於:

(當然需要更多的錯誤處理,但你的想法。)

+0

感謝@McGarnagle,但有多個「item」標籤,每個標籤都有一個單獨的「主」。 – AliR

+0

@AliRajput oops,我沒有捕捉到 - 上面更新。你可以使用'元素',然後使用另一個Linq選擇。 – McGarnagle

+0

感謝您的輸入,這對我無效 – AliR

0

測試了一個工作。沒有定義一些額外的類,這是不可能的。在這裏,我有一個支點接口Item,然後有兩個類實現接口AdditemMainItem

隨意問任何部分的解釋。

// Since there are different types of items, we need an interface/abstact 
// class to pivot. 
public interface Item { 
} 

// The information neccesary for storing the 'Extra' element. 
public class Extra { 
    public Int32 ID { get; private set; } 
    public Int32 Quantity { get; private set; } 
    public String Description { get; private set; } 

    public Extra(XElement extra) { 

     // Here we load up all of the details from the 'extra' element 
     this.ID = Int32.Parse(extra.Attribute("id").Value); 
     this.Quantity = Int32.Parse(extra.Element("Qty").Value); ; 
     this.Description = extra.Element("Desc").Value; 
    } 
} 

// The 'add-item' is associated with the 'add' tag in the actual XML. 
public class AddItem : Item { 

    public IEnumerable<Extra> Extras { get; private set; } 

    // The 'extras' is a collection of many items, so we require 
    // an ienumerable. 
    public AddItem(IEnumerable<Extra> extras) { 
     this.Extras = extras; 
    } 

} 

// The storage for the 'main-item' 
public class MainItem : Item { 
    public Int32 ID { get; private set; } 
    public Int32 Quantity { get; private set; } 

    public MainItem(Int32 id, Int32 quantity) { 
     this.ID = id; 
     this.Quantity = quantity; 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     String data = File.ReadAllText("File.txt"); 

     XElement tree = XElement.Parse(data); 


     var projection = tree.Elements() 
      .Select(invoice => new { 
       // Project the main details of the invoice { OrderID, Time, Date, Order } 
       // The order itself needs to be projected again though because it too is a 
       // collection of sub items. 
       OrderID = invoice.Element("order_id").Value, 
       Time = invoice.Element("time").Value, 
       Date = invoice.Element("date").Value, 
       Order = invoice.Element("order") 
        .Elements() 
        .Elements() 
        .Select(item => { 

         // First, we need to know what type of item this 'order' is. 
         String itemType = item.Name.ToString(); 

         // If its a 'main' item, then return that type. 
         if (itemType == "Main") { 
          Int32 id = Int32.Parse(item.Element("id").Value); 
          Int32 quantity = Int32.Parse(item.Element("Qty").Value); 

          return (Item)new MainItem(id, quantity); 
         } 

         // If it's an 'Add' item. Then we have to: 
         if (itemType == "Add") { 
          // (1) Capture all of the extras. 
          IEnumerable<Extra> extras = item.Elements() 
           .Select(extra => new Extra(extra)) 
           .ToList(); 

          // (2) Add the extras to a new AddItem. Then return the 'add'-item. 
          // Notice that we have to cast to 'Item' because we are returning 
          // a 'Main'-item sometimes and an 'add' item other times. 
          // Select requires the return type to be the same regardless. 
          return (Item)new AddItem(extras); 
         } 

         // Hopefully this path never hits. 
         throw new NotImplementedException("This path not defined"); 

        }).ToList() 

      }).ToList(); 

     Console.WriteLine(projection); 
    } 
} 
+0

感謝您的輸入,我沒有' (暫時)不能改變我的代碼的其餘部分。 – AliR

0

如果您在查詢中使用某些xpath,則可以使事情更易於管理。如果你問我,在這裏使用純LINQ to XML可能會變得太冗長。

var query = 
    from invoice in doc.XPathSelectElements("/invoices/invoice") 
    select new 
    { 
     OrderId = (int)invoice.Element("order_id"), 
     Time = (string)invoice.Element("time"), 
     Date = (string)invoice.Element("date"), 
     Items = 
      from item in invoice.XPathSelectElements("./order/item") 
      select new 
      { 
       Id = (int)item.XPathSelectElement("./Main/id"), 
       Quantity = (int)item.XPathSelectElement("./Main/Qty"), 
       Extras = 
        from extra in item.XPathSelectElements("./Add/Extra") 
        select new 
        { 
         Id = (int)extra.Attribute("id"), 
         Quantity = (int)extra.Element("Qty"), 
         Description = (string)extra.Element("Desc"), 
        }, 
      }, 
    }; 
+0

感謝您的輸入;我避免了這一點解決方案只是不開始學習XPath,因爲我剛剛開始使用LINQ to XML,所以現在使用XDocument – AliR

1

這裏被解析你的XML的:

var parser = new Parser(); 
XDocument xdoc = XDocument.Load(path_to_xml); 
var orders = from invoice in xdoc.Root.Elements() 
      select parser.ParseOrderFrom(invoice); 

這就是所有。我創建了以下課程。訂單,其中包含訂單項目的集合,並具有良好的解析日期:

public class Order 
{ 
    public int Id { get; set; } 
    public DateTime Date { get; set; } 
    public List<OrderItem> Items { get; set; } 
} 

訂單項,這是您的主菜。此外,它具有額外的名單內(如果有的話):

public class OrderItem 
{ 
    public int Id { get; set; } 
    public int Quantity { get; set; } 
    public List<Extra> Extras { get; set; } 
} 

和額外類:

public class Extra 
{ 
    public int Id { get; set; } 
    public int Quantity { get; set; } 
    public string Description { get; set; } 
} 

發生在單獨的語法分析器類中的所有解析,如果你想(這將讓域類清潔):

public class Parser 
{ 
    public Order ParseOrderFrom(XElement invoice) 
    { 
     string time = (string)invoice.Element("time"); 
     string date = (string)invoice.Element("date"); 

     return new Order { 
      Id = (int)invoice.Element("order_id"), 
      Date = DateTime.ParseExact(date + time, "dd-MM-yyyyhh:mm tt", null), 
      Items = invoice.Element("order").Elements("item") 
          .Select(i => ParseOrderItemFrom(i)).ToList() 
     }; 
    } 

    public OrderItem ParseOrderItemFrom(XElement item) 
    { 
     var main = item.Element("Main"); 

     return new OrderItem { 
      Id = (int)main.Element("id"), 
      Quantity = (int)main.Element("Qty"), 
      Extras = item.Element("Add").Elements("Extra") 
         .Select(e => ParseExtraFrom(e)).ToList() 
     }; 
    } 

    public Extra ParseExtraFrom(XElement extra) 
    { 
     return new Extra { 
      Id = (int)extra.Attribute("id"), 
      Quantity = (int)extra.Element("Qty"), 
      Description = (string)extra.Element("Desc") 
     }; 
    } 
} 
+0

非常感謝,這對解析值類型非常有幫助。一旦我編寫將數據保存到數據庫的代碼,我將再次檢查 – AliR

+1

我喜歡這種方法。 – sircodesalot

+0

@AliRajput我更新瞭解析器和訂單項來處理額外的列表。順便說一句,這很容易,因爲所有的訂單項解析處理在不同的方法 –