簡體   English   中英

使用LINQ和VB.NET的XML

[英]XML using LINQ with VB.NET

下面是我的xml的副本。 基本上,我在每種作物中都有不同的農作物(稻谷,大豆,玉米等),我有一個田間節點,並且在該節點內有多個描述田間的節點。 我還有一個費用節點,其中包含該字段節點中的多個費用。 每個字段內的費用可能有所不同,這就是為什么我有一個可以包含所有費用的節點的原因。 我想做的是找到一種查詢這些東西的簡便方法。 可以說我想獲取一個特定字段的所有信息。 好在我的應用程序中,如果他們選擇某種作物,那么我需要在組合框中列出所有字段。 我的結構正確還是有其他方法可以做到這一點? 如果我想將特定作物的所有田地的所有費用加起來怎么辦,可以說有20到30個田地呢? 我怎樣才能做到這一點? 我知道如何選擇值=“某物”的某些東西,但我需要做的主要事情是選擇特定作物的所有字段並添加指數。 或者,如果我在應用程序中選擇了作物和田地,那么我需要能夠從田地節點提取所有數據。 任何幫助將是巨大的!

<Data>
 <Crop>
  <Name>Corn</Name>
    <Field>
      <Name>Field1</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
     <Field>
      <Name>Field2</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
  </Crop>
  <Crop>
  <Name>Soybeans</Name>
    <Field>
      <Name>Field2</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
     <Field>
      <Name>Field1</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
  </Crop>
  <Crop>
   <Name>Rice</Name>
    <Field>
      <Name>Field3</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
     <Field>
      <Name>Field4</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
     <Field>
      <Name>Field5</Name>
      <Acres></Acres>
      <Bushels></Bushels>
        <Expenses>
           <expense1></expense1>
           <expense2></expense2>
           etc....
        </Expenses>
     </Field>
  </Crop>

您的XML結構很有意義(盡管我做了一些下面列出的修改)。 像數據庫一樣,XML數據的結構應使其所代表的關系和數據具有邏輯意義。 XML也應該是可讀的,您似乎也是如此。 話雖這么說,有很多方法可以“剝皮”,在這里我將概述其中的兩種。

這是修改后的XML文件:

<Data>
    <Crops>
        <Crop>
            <Name>Corn</Name>
            <Fields>
                <Field>
                    <Name>Field1</Name>
                    <Acres>2</Acres>
                    <Bushels>15</Bushels>
                    <Expenses>
                        <expense>12.0</expense>
                        <expense>20.0</expense>
                    </Expenses>
                </Field>
                <Field>
                    <Name>Field2</Name>
                    <Acres>30</Acres>
                    <Bushels>60</Bushels>
                    <Expenses>
                        <expense>45.0</expense>
                        <expense>70.0</expense>
                    </Expenses>
                </Field>
            </Fields>
        </Crop>
        <Crop>
            <Name>Soybeans</Name>
            <Fields>
                <Field>
                    <Name>Field2</Name>
                    <Acres>5</Acres>
                    <Bushels>1</Bushels>
                    <Expenses>
                        <expense>33.0</expense>
                        <expense>71.0</expense>
                    </Expenses>
                </Field>
                <Field>
                    <Name>Field1</Name>
                    <Acres>10</Acres>
                    <Bushels>20</Bushels>
                    <Expenses>
                        <expense>15.0</expense>
                        <expense>10.0</expense>
                    </Expenses>
                </Field>
            </Fields>
        </Crop>
        <Crop>
            <Name>Rice</Name>
            <Fields>
                <Field>
                    <Name>Field3</Name>
                    <Acres>15</Acres>
                    <Bushels>40</Bushels>
                    <Expenses>
                        <expense>15.50</expense>
                        <expense>44.79</expense>
                    </Expenses>
                </Field>
                <Field>
                    <Name>Field4</Name>
                    <Acres>4</Acres>
                    <Bushels>18</Bushels>
                    <Expenses>
                        <expense>0.0</expense>
                        <expense>21.0</expense>
                    </Expenses>
                </Field>
                <Field>
                    <Name>Field5</Name>
                    <Acres>15</Acres>
                    <Bushels>4</Bushels>
                    <Expenses>
                        <expense>62.0</expense>
                        <expense>27.45</expense>
                    </Expenses>
                </Field>
            </Fields>
        </Crop>
    </Crops>
</Data>

我將這兩個都創建為單元測試,因此它們都應按原樣運行。

第一個使用LINQ(根據您的問題的要求)如下所示。 一般的策略是將xml文件加載到XmlDocument實例中,並使用XPath語法查詢節點並遍歷文檔。

Dim xmlData As String = "<Data><Crops><Crop><Name>Corn</Name><Fields><Field><Name>Field1</Name><Acres>2</Acres><Bushels>15</Bushels><Expenses><expense>12.0</expense><expense>20.0</expense></Expenses></Field><Field><Name>Field2</Name><Acres>30</Acres><Bushels>60</Bushels><Expenses><expense>45.0</expense><expense>70.0</expense></Expenses></Field></Fields></Crop><Crop><Name>Soybeans</Name><Fields><Field><Name>Field2</Name><Acres>5</Acres><Bushels>1</Bushels><Expenses><expense>33.0</expense><expense>71.0</expense></Expenses></Field><Field><Name>Field1</Name><Acres>10</Acres><Bushels>20</Bushels><Expenses><expense>15.0</expense><expense>10.0</expense></Expenses></Field></Fields></Crop><Crop><Name>Rice</Name><Fields><Field><Name>Field3</Name><Acres>15</Acres><Bushels>40</Bushels><Expenses><expense>15.50</expense><expense>44.79</expense></Expenses></Field><Field><Name>Field4</Name><Acres>4</Acres><Bushels>18</Bushels><Expenses><expense>0.0</expense><expense>21.0</expense></Expenses></Field><Field><Name>Field5</Name><Acres>15</Acres><Bushels>4</Bushels><Expenses><expense>62.0</expense><expense>27.45</expense></Expenses></Field></Fields></Crop></Crops></Data>"
Dim doc As New System.Xml.XmlDocument()
doc.LoadXml(xmlData) 'Load the data from the string'
'If loading from a file, use this'
'doc.Load("C:\MyFileName.xml")'
Dim cropNodes As Xml.XmlNodeList = doc.SelectNodes("//Crop/Name") 'Use XPath syntax for querying'
Dim cropNames As New List(Of String)()
'Here, Linq is used to get all of the values of the name nodes within each crop (assuming each crop has only one name).'
cropNames.AddRange((From x As Xml.XmlNode In cropNodes Select x.InnerText).ToArray())

cropNames.ForEach(Sub(crop)
            Debug.WriteLine("Crop: " & crop)
        End Sub)

Dim fieldData As Xml.XmlNodeList = doc.SelectNodes("//Field")
For Each item As Xml.XmlNode In fieldData
    Debug.WriteLine("Field Name: " & item.SelectSingleNode("Name").InnerText())
Next

'Get expenses per field for each crop'
For Each name As String In cropNames
    Dim cropNode As Xml.XmlNode = doc.SelectSingleNode("//Crop/Name[text()='" & name & "']/..")
    Debug.WriteLine("Num Fields: " & cropNode.SelectNodes("Fields/Field").Count)
    For Each node As Xml.XmlNode In cropNode.SelectNodes("Fields/Field")
        Debug.WriteLine("Total Expenses for " & node.SelectSingleNode("Name").InnerText & ":$" & (From item As Xml.XmlNode In node.SelectNodes("Expenses/expense") Select Double.Parse(item.InnerText)).Sum())
    Next
Next
Debug.WriteLine("Done")

下一步(這是我的偏愛)將是將XML反序列化為強類型的.NET對象。 這樣,您可以在每個類中創建函數來為您完成工作,並且完全避免使用XPath語法,並且添加自定義功能變得更加容易。

這是一個例子:

'In some test code:
Dim xmlData As String = "<Data><Crops><Crop><Name>Corn</Name><Fields><Field><Name>Field1</Name><Acres>2</Acres><Bushels>15</Bushels><Expenses><expense>12.0</expense><expense>20.0</expense></Expenses></Field><Field><Name>Field2</Name><Acres>30</Acres><Bushels>60</Bushels><Expenses><expense>45.0</expense><expense>70.0</expense></Expenses></Field></Fields></Crop><Crop><Name>Soybeans</Name><Fields><Field><Name>Field2</Name><Acres>5</Acres><Bushels>1</Bushels><Expenses><expense>33.0</expense><expense>71.0</expense></Expenses></Field><Field><Name>Field1</Name><Acres>10</Acres><Bushels>20</Bushels><Expenses><expense>15.0</expense><expense>10.0</expense></Expenses></Field></Fields></Crop><Crop><Name>Rice</Name><Fields><Field><Name>Field3</Name><Acres>15</Acres><Bushels>40</Bushels><Expenses><expense>15.50</expense><expense>44.79</expense></Expenses></Field><Field><Name>Field4</Name><Acres>4</Acres><Bushels>18</Bushels><Expenses><expense>0.0</expense><expense>21.0</expense></Expenses></Field><Field><Name>Field5</Name><Acres>15</Acres><Bushels>4</Bushels><Expenses><expense>62.0</expense><expense>27.45</expense></Expenses></Field></Fields></Crop></Crops></Data>"
Dim crops As CropData = Nothing

Dim ser As New Xml.Serialization.XmlSerializer(GetType(CropData))
Try
    crops = ser.Deserialize(Xml.XmlReader.Create(New IO.StringReader(xmlData)))
    For Each item In crops.Crops
        Debug.WriteLine("Total Expenses for crop: " & item.Name & ": $" & item.GetTotalExpenses())
    Next
Catch ex As Exception

End Try

'Other classes:
<Serializable()>
<Xml.Serialization.XmlRoot("Data")>
Public Class CropData

    'XmlIgnore is somewhat unnecessary on a private instance, but I like to be explicit
    <Xml.Serialization.XmlIgnore()> _
    Private _crops As List(Of Crop)

    Public Sub New()
        _crops = New List(Of Crop)()
    End Sub

    <Xml.Serialization.XmlArray(ElementName:="Crops"), Xml.Serialization.XmlArrayItem("Crop")>
    Public Property Crops As List(Of Crop)
        Get
            Return _crops
        End Get
        Set(value As List(Of Crop))
            If value IsNot Nothing Then
                _crops = value
            End If
        End Set
    End Property

End Class

<Serializable()>
Public Class Crop

    <Xml.Serialization.XmlIgnore()>
    Private _fields As List(Of Field)

    Public Sub New()
        _fields = New List(Of Field)()
    End Sub

    Public Function GetTotalExpenses() As Double
        Dim total As Double = 0D
        If Me.Fields.Any() Then
            Me.Fields.ForEach(Sub(item)
                total += item.GetFieldExpenses()
            End Sub)
        End If
        Return total
    End Function

    <Xml.Serialization.XmlElement(ElementName:="Name")>
    Public Property Name As String

    <Xml.Serialization.XmlArray(ElementName:="Fields"), Xml.Serialization.XmlArrayItem(ElementName:="Field")>
    Public Property Fields As List(Of Field)
        Get
            Return _fields
        End Get
        Set(value As List(Of Field))
            If value IsNot Nothing Then
                _fields = value
            End If
        End Set
    End Property

End Class

<Serializable()>
Public Class Field

    Public Sub New()

    End Sub

    Public Function GetFieldExpenses() As Double
        If Me.Expenses.Any() Then
            Return Me.Expenses.Sum()
        Else
            Return 0
        End If
    End Function


    <Xml.Serialization.XmlElement(ElementName:="Name")>
    Public Property Name As String
    <Xml.Serialization.XmlElement(ElementName:="Acres")>
    Public Property Acres As Double
    <Xml.Serialization.XmlElement(ElementName:="Bushels")>
    Public Property Bushels As Double

    <Xml.Serialization.XmlArray("Expenses"), Xml.Serialization.XmlArrayItem("expense")>
    Public Property Expenses As List(Of Double)

End Class

查看Linq to XmlVB的xml運算符以獲取簡潔的解決方案:

Function TotalExpenses(myData as XElement, myCropName as String) as Double
    Dim expenseElements = (From crop In myData.<Crop>
                          Where crop.<Name>.FirstOrDefault().Value = myCropName
                          From expenseElement In element.Descendants("Expenses").Descendants()
                          Select expenseElement)

    Return expenseElements.Sum(Function(ee) Double.Parse(ee.Value))
End Function

如果要使用Vb.net加載XML文件,請嘗試使用數據功能區上的XML功能將其加載到Excel中,以檢查其格式是否正確。 無法加載您的格式(清理了等的格式。)。 但是,以下方法似乎有效:

<TblCrops>
<Field>
<Crop>Corn</Crop>
<Name>Field1</Name>
<Acres>120</Acres>
<Bushels>1000</Bushels>
<Expenses>
<expense1>100</expense1>
<expense2>200</expense2>
</Expenses>
</Field>
<Field>
<Crop>Corn</Crop>
<Name>Field2</Name>
<Acres>130</Acres>
<Bushels>1100</Bushels>
<Expenses>
<expense1>110</expense1>
<expense2>210</expense2>
</Expenses>
</Field>
</TblCrops>
<TblCrops>
<Field>
<Crop>Corn</Crop>
<Name>Field1</Name>
<Acres>120</Acres>
<Bushels>1000</Bushels>
<Expenses>
<expense1>100</expense1>
<expense2>200</expense2>
</Expenses>
 </Field>
<Field>
<Crop>Corn</Crop>
<Name>Field2</Name>
<Acres>130</Acres>
<Bushels>1100</Bushels>
<Expenses>
<expense1>110</expense1>
<expense2>210</expense2>
</Expenses>
</Field>
</TblCrops>

然后,您可以對作物字段執行選擇查詢,並將所有費用字段加在一起。 如果您不希望每條記錄的字段數發生變化,則可以根據費用的數量將費用拉到另一個字段中,並通過隨機生成的記錄定位符將它們關聯起來。 IE

 <TblCrops>
 <Field>
 <Crop>Corn</Crop>
 <Name>Field1</Name>
 <Acres>120</Acres>
 <Bushels>1000</Bushels>
 <ExpenseID>106724</ExpenseID>
 </Field>
 <Field>
 <Crop>Corn</Crop>
 <Name>Field2</Name>
 <Acres>130</Acres>
 <Bushels>1100</Bushels>
 <ExpenseID>879065</ExpenseID>
 </Field>
 </TblCrops>

<TblExpenses>
<Expense>
<ExpenseID>106724</ExpenseID>
<amount>xxx</amount>
</Expense>
<Expense>
<ExpenseID>879065</ExpenseID>
<amount>yyy</amount>
</Expense>
<ExpenseID>879065</ExpenseID>
<amount>zzz</amount>
</Expense>
</TblExpenses>

然后,您將需要一些LEFT OUTER JOIN技巧,並在該字段上使用一條select語句,以從這兩個表中產生費用的最終記錄集。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM