共用方式為


HOW TO:具有標頭資訊存取權的資料流 XML 片段

更新: November 2007

有時候您必須讀取任意大的 XML 檔案並撰寫您的應用程式,讓應用程式的記憶體使用量可以預測。如果您嘗試使用大型 XML 檔案填入 XML 樹狀結構,您的記憶體使用量將與檔案大小成正比,也就是,變成過度。因此,您應該改用資料流技術。

其中一個選項是使用 XmlReader 撰寫您的應用程式。但是,您可能想要使用 LINQ 查詢 XML 樹狀結構。若發生這種情況,您可以撰寫自己的自訂座標軸方法。如需詳細資訊,請參閱 HOW TO:撰寫 LINQ to XML 座標軸方法

若要撰寫您自己的座標軸方法,您可以撰寫使用 XmlReader 讀取節點的小方法,直到該方法到達您感興趣的其中一個節點。然後,該方法會呼叫從 XmlReader 讀取的 ReadFrom,並具現化 XML 片段。接著,它會針對列舉自訂座標軸方法的方法,透過 yield return 產生每個片段。此時,您就可以在自訂座標軸方法上撰寫 LINQ 查詢。

在您僅需要處理一次來源文件的情況下,最適合使用資料流技術,而且您可以用文件的順序處理項目。特定的標準查詢運算子 (例如,OrderBy) 會反覆查看其來源、收集所有資料、排序這些資料,最後產生順序中的第一個項目。請注意,如果您在產生第一個項目前使用具體化其來源的查詢運算子,您將不會保留小的記憶體使用量。

範例

有時候問題會變得更有趣。在下列 XML 文件中,您自訂座標軸方法的消費者也必須知道每個項目所屬客戶的名稱。

<?xml version="1.0" encoding="utf-8" ?>
<Root>
  <Customer>
    <Name>A. Datum Corporation</Name>
    <Item>
      <Key>0001</Key>
    </Item>
    <Item>
      <Key>0002</Key>
    </Item>
    <Item>
      <Key>0003</Key>
    </Item>
    <Item>
      <Key>0004</Key>
    </Item>
  </Customer>
  <Customer>
    <Name>Fabrikam, Inc.</Name>
    <Item>
      <Key>0005</Key>
    </Item>
    <Item>
      <Key>0006</Key>
    </Item>
    <Item>
      <Key>0007</Key>
    </Item>
    <Item>
      <Key>0008</Key>
    </Item>
  </Customer>
  <Customer>
    <Name>Southridge Video</Name>
    <Item>
      <Key>0009</Key>
    </Item>
    <Item>
      <Key>0010</Key>
    </Item>
  </Customer>
</Root>

這個範例所採取的方法也是監看這個標頭資訊、儲存標頭資訊,然後建置同時包含您所列舉之標頭資訊與細節的小型 XML 樹狀結構。這個座標軸方法接著就會產生這個新的小型 XML 樹狀結構。該查詢將可以存取標頭資訊以及詳細資訊。

此方法擁有小的記憶體使用量。產生每個細節 XML 片段時,不會針對上一個片段保留任何參考,而且這個片段適用於記憶體回收。請注意,這個技術會在堆積上建立許多短期存在的物件。

下列範例顯示如何實作與使用從 URI 指定之檔案資料流 XML 片段的自訂座標軸方法。這個自訂座標軸是特別撰寫的,讓它預備擁有 Customer、Name 和 Item 項目的文件,並預期這些項目將會與上述 Source.xml 文件的排列方式相同。這是簡化的實作方法。較為複雜的實作方法則用於剖析無效的文件。

注意事項:

下列範例使用 C# 的 yield return 建構函式。在 Visual Basic 2008 中沒有同等的功能,因此,只有在 C# 中提供這個範例。

static IEnumerable<XElement> StreamCustomerItem(string uri)
{
    using (XmlReader reader = XmlReader.Create(uri))
    {
        XElement name = null;
        XElement item = null;

        reader.MoveToContent();

        // Parse the file, save header information when encountered, and yield the
        // Item XElement objects as they are created.

        // loop through Customer elements
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element
                && reader.Name == "Customer")
            {
                // move to Name element
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element &&
                        reader.Name == "Name")
                    {
                        name = XElement.ReadFrom(reader) as XElement;
                        break;
                    }
                }
                
                // loop through Item elements
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;
                    if (reader.NodeType == XmlNodeType.Element
                        && reader.Name == "Item")
                    {
                        item = XElement.ReadFrom(reader) as XElement;
                        if (item != null) {
                            XElement tempRoot = new XElement("Root",
                                new XElement(name)
                            );
                            tempRoot.Add(item);
                            yield return item;
                        }
                    }
                }
            }
        }
    }
}

static void Main(string[] args)
{
    XElement xmlTree = new XElement("Root",
        from el in StreamCustomerItem("Source.xml")
        where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7
        select new XElement("Item",
            new XElement("Customer", (string)el.Parent.Element("Name")),
            new XElement(el.Element("Key"))
        )
    );
    Console.WriteLine(xmlTree);
}

此程式碼會產生下列輸出:

<Root>
  <Item>
    <Customer>A. Datum Corporation</Customer>
    <Key>0003</Key>
  </Item>
  <Item>
    <Customer>A. Datum Corporation</Customer>
    <Key>0004</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0005</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0006</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0007</Key>
  </Item>
</Root>

請參閱

概念

LINQ to XML 進階程式設計