Paginacja za pomocą XmlReadera
class Program {
public static void Main(string[] args) {
Console.WriteLine(new Program().GetPage(@"test.xml", 2, 7).OuterXml);
}
// Zwraca dokument Xml zawierajacy rekordy dla zadanej strony
private XmlDocument GetPage(string inputUri, int pageNumber, int pageSize) {
if(pageNumber < 1) {
throw new ArgumentException("Invalid pageNumber.", "pageNumber");
}
if(pageSize < 1) {
throw new ArgumentException("Incorrect pageSize.", "pageSize");
}
// Tutaj bedzie zapisany wynikowy Xml zawierajacy rekordy dla zadanej strony
XmlDocument pageXml = new XmlDocument();
pageXml.LoadXml("<page />");
// Ustaw XmlReadera na pierwszy element potomny elementu root
using(XmlReader xr = XmlReader.Create(inputUri)) {
while(xr.Depth == 0) {
if(!xr.Read()) {
return pageXml;
}
}
// Przeskocz rekordy ktore nie maja byc wyswietlone. W przypadku osiagniecia konca
// pliku stworz ostatnia strone i zapisz w zmiennej pageXml.
SkipRecords(pageXml, xr, (pageNumber - 1) * pageSize, pageSize);
// Jesli pageXml nie zawiera jeszcze wynikow (bedacych ostatnia strona)
if(!pageXml.DocumentElement.HasChildNodes) {
// Stworz wyniki dla biezacej strony
for(int nodeNum = 0; nodeNum < pageSize && !xr.EOF; nodeNum++) {
using(XmlReader currRecordReader = xr.ReadSubtree()) {
XmlNode xn = pageXml.ReadNode(currRecordReader);
pageXml.DocumentElement.AppendChild(xn);
currRecordReader.Close();
}
xr.Skip();
ReadToNextElement(xr);
}
}
}
return pageXml;
}
// Funkcja przeskakuje rekordy ktorych nie chcemy wyswietlac. Po wyjsciu z funkcji XmlReader
// jest ustawiony na elemencie bedacym pierwszym rekordem do wyswietlenia. Jesli liczba rekordow
// do przeskoczenia jest wieksza od liczby rekordow w dokumencie rekordy z ostatniej strony
// dodawane są do obiektu pageXml.
private void SkipRecords(XmlDocument pageXml, XmlReader xr, int numNodesToSkip, int pageSize) {
Debug.Assert(pageSize > 0, "pageSize has to greater than 0");
// tablica zawiera wezly dla aktualnie przetwarzanej strony na wypadek gdyby ta strona
// okazala sie ostatnia (np. gdy liczba rekordów do przeskoczenia jest wieksza od
// liczby wszystkich rekordow)
string[] potentialLastPage = new string[pageSize];
int nodeIdx = 0;
for(; nodeIdx < numNodesToSkip; nodeIdx++) {
// Ustaw XmlReadera na nastepnym wezle typu element
ReadToNextElement(xr);
if(xr.EOF) {
// Osiagnelismy koniec dokumentu
break;
}
// Zachowaj rekord na wypadek gdyby strona okazala sie ostatnia
potentialLastPage[nodeIdx % pageSize] = xr.ReadOuterXml();
}
// Ustaw XmlReadera na nastepnym wezle typu element. Jesli po tej operacji
// xr.EOF == false to XmlReader jest ustawiony na pierwszym rekordzie "do wyswietlenia"
ReadToNextElement(xr);
// Osiagniety koniec dokumentu - stworz ostatnia strone
if(xr.EOF) {
pageXml.DocumentElement.InnerXml =
string.Join(string.Empty, potentialLastPage, 0, nodeIdx % pageSize == 0 ? pageSize : nodeIdx % pageSize);
}
}
// Ustawia XmlReader na najblizszym wezle typu Element
private void ReadToNextElement(XmlReader xr) {
while(xr.NodeType != XmlNodeType.Element && !xr.EOF) {
xr.Read();
}
}
}