| 
	
		|  |  |  
		|  | 
	Описание для автора не найдено
 |  |  
		|  |  |  |  
	
		|  |  |  
		|  | 
	
Эффективные техники редактирования больших XML-файлов Автор: Деа Обасаньо (Dare Obasanjo), Корпорация Microsoft 
(http://www.microsoft.com/)
 Перевод: Шатохина Надежда(sna@uneta.org), UNETA 
(http://www.uneta.org/)
 Апрель 2004 Обзор:Деа Обасаньо показывает две 
техники для эффективного обновления или модифицирования больших XML-файлов, 
таких как файл журнала и копии баз данных.
 
Введение 
Использование техник включения XML 
Связывание XmlReader с XmlWriter 
Благодарность 
Введение Поскольку XML обрел популярность как формат представления больших источников 
информации, у разработчиков появилась проблема редактирования больших 
XML-файлов. Это особенно касается приложений, которые обрабатывают большие файлы 
журналов и должны постоянно добавлять информацию в эти файлы. Самый простой путь 
редактирования XML-файла — загрузить его в XmlDocument, 
модифицировать этот документ в памяти и затем опять сохранить на диск. Однако 
это означает, что весь XML-документ должен быть загружен в память, что может 
быть невозможным из-за размера документа и требований приложения к объему и 
конфигурации памяти. В этой статье показаны альтернативные подходы к редактированию XML-документа, 
которые не используют его загрузку в экземпляр 
XmlDocument. 
Использование техник включения XML Первый предлагаемый мною подход более всего будет полезен для внесения 
дополнительных значений в XML-файл журнала. Обычно разработчики сталкиваются с 
такой проблемой: необходима возможность просто дополнять файл журнала новыми 
элементами, а не загружать для этого весь документ в память. Из-за формальной 
строгости правил XML зачастую трудно использовать традиционные средства для 
добавления элементов в XML-файл журнала таким способом, который не делает файл 
журнала завершенным, из-за их плохой сформированности. Первая техника, которую я покажу, ориентирована на ситуации, когда необходимо 
иметь возможность быстро добавлять элементы в XML-документ. Этот подход включает 
создание двух файлов. Первый файл — правильный XML-файл, второй — фрагмент XML. 
Правильный XML-файл включает фрагмент XML, используя или внешнюю 
сущность, объявленную в DTD, или применяя элемент xi:include. Таким образом, файл, включающий фрагмент XML, может 
быть эффективно обновлен простым дополнением во время обработки с использованием 
включающего файла. Примеры включающего и включаемого файла показаны ниже: Logfile.xml: 
<?xml version="1.0"?>
<!DOCTYPE logfile [
<!ENTITY events    
 SYSTEM "logfile-entries.txt">
]>
<logfile>
&events;
</logfile>
Logfile-events.txt:
<event>
 <ip>127.0.0.1</ip>
 <http_method>GET</http_method>
 <file>index.html</file>
 <date>2004-04-01T17:35:20.0656808-08:00</date>
</event>
<event>
 <ip>127.0.0.1</ip>
 <http_method>GET</http_method>
 <file>stylesheet.css</file>
 <date>2004-04-01T17:35:23.0656120-08:00</date>
 <referrer>http://www.example.com/index.html</referrer>
</event>
<event>
  <ip>127.0.0.1</ip>
  <http_method>GET</http_method>
  <file>logo.gif</file>
  <date>2004-04-01T17:35:25.238220-08:00</date>
  <referrer>http://www.example.com/index.html</referrer>
</event>
Файл logfile-entries.txt включает фрагмент XML и может быть эффективно 
обновлен с помощью обычных методов ввода/вывода файла. Следующий код показывает, 
как можно ввести элемент в XML-файл журнала путем добавления его в конец 
текстового файла: using System;
using System.IO;
using System.Xml; 
public class Test{ 
  public static void Main(string[] args){
    StreamWriter sw = File.AppendText("logfile-entries.txt");
    XmlTextWriter xtw =  new XmlTextWriter(sw); 
    xtw.WriteStartElement("event"); 
    xtw.WriteElementString("ip", "192.168.0.1");
    xtw.WriteElementString("http_method", "POST");
    xtw.WriteElementString("file", "comments.aspx");
    xtw.WriteElementString("date", "1999-05-05T19:25:13.238220-08:00");    
    xtw.Close();
                 
  }
}
Как только элементы добавлены в текстовый файл, их можно обрабатывать из 
XML-файла журнала с помощью традиционных техник обработки XML. Следующий код 
использует XPath для перебора зарегистрированных в журнале событий в файле 
logfile.xml, составляя список файлов, к которым был осуществлен доступ, и 
записывая, когда был осуществлен доступ. using System;
using System.Xml; 
public class Test2{
 
  public static void Main(string[] args){
    XmlValidatingReader vr = 
    new XmlValidatingReader(new XmlTextReader("logfile.xml"));
    vr.ValidationType = ValidationType.None;          
    vr.EntityHandling = EntityHandling.ExpandEntities; 
    XmlDocument doc = new XmlDocument(); 
    doc.Load(vr); 
    foreach(XmlElement element in doc.SelectNodes("//event")){
      
      string file = element.ChildNodes[2].InnerText; 
      string date = element.ChildNodes[3].InnerText; 
      
      Console.WriteLine("{0} accessed at {1}", file, date);
    }                 
  }
} 
В результате выполнения приведенного выше кода формируется следующий 
результат: index.html accessed at 2004-04-01T17:35:20.0656808-08:00
stylesheet.css accessed at 2004-04-01T17:35:23.0656120-08:00
logo.gif accessed at 2004-04-01T17:35:25.238220-08:00
comments.aspx accessed at 1999-05-05T19:25:13.238220-08:00
 
Связывание XmlReader с XmlWriter В определенных случаях может возникнуть желание осуществить более сложные 
манипуляции с XML-файлом, кроме добавления элементов в корневой элемент. 
Например, кто-то перед архивированием файла журнала захочет отфильтровать каждый 
его элемент, не отвечающий некоторым определенным критериям. Одним из подходов 
для этого может быть загрузка XML-файла в XmlDocument и 
последующий выбор интересующих событий с помощью XPath. Однако это подразумевает 
загрузку в память всего документа, что может быть недопустимым, если документ 
большой. Другой вариант для решения таких задач использует XSLT, но он страдает 
от той же проблемы, что и подход с XmlDocument, поскольку весь 
документ должен находиться в памяти. Также разработчикам, плохо знакомым с XSLT, 
необходимо время на понимание того, как правильно использовать подходящий 
шаблон. Одно из решений проблемы обработки очень большого XML-документа — прочитать 
XML с помощью XmlReader и переписать после прочтения с помощью 
XmlWriter. При этом никогда весь документ сразу не находится в 
памяти, и в XML может быть подвергнут даже более тонким операциям, чем простое 
добавление элементов. Следующий пример кода читает XML-документ из предыдущей 
секции и сохраняет его как файл архива после извлечения всех событий, чей 
элемент ip имеет значение «127.0.0.1». using System;
using System.Xml; 
using System.IO;
using System.Text;
public class Test2{
  static string ipKey;
  static string httpMethodKey;
  static string fileKey; 
  static string dateKey;
  static string referrerKey; 
  public static void WriteAttributes(XmlReader reader, XmlWriter writer){
    
    if(reader.MoveToFirstAttribute()){
      do{
   writer.WriteAttributeString(reader.Prefix, 
                reader.LocalName, 
                reader.NamespaceURI,
                reader.Value); 
      }while(reader.MoveToNextAttribute());
      reader.MoveToElement(); 
    }
  }
  public static void WriteEvent(XmlWriter writer, string ip,
                                 string httpMethod, string file,
                                 string date, string referrer){
    
    writer.WriteStartElement("event"); 
    writer.WriteElementString("ip", ip);
    writer.WriteElementString("http_method", httpMethod);
    writer.WriteElementString("file", file);
    writer.WriteElementString("date", date);    
    if(referrer != null) writer.WriteElementString("referrer", referrer);
    writer.WriteEndElement(); 
  } 
  public static void ReadEvent(XmlReader reader, out string ip,
                              out string httpMethod, out string file,
                              out string date, out string referrer){
    ip = httpMethod = file = date = referrer = null; 
    while( reader.Read() && reader.NodeType != XmlNodeType.EndElement){                
      
 if (reader.NodeType == XmlNodeType.Element) {
          
     if(reader.Name == ipKey){   
       ip = reader.ReadString(); 
     }else if(reader.Name == httpMethodKey){ 
       httpMethod = reader.ReadString();
     }else if(reader.Name == fileKey){ 
       file = reader.ReadString();
     }else if(reader.Name == dateKey){ 
       date = reader.ReadString();
       // reader.Read(); // читаем закрывающий тэг
     }else if(reader.Name == referrerKey){ 
       referrer = reader.ReadString();
      }
        }//if 
    }//while   
  }
  public static void Main(string[] args){
    string ip, httpMethod, file, date, referrer; 
    //инициализируем XmlNameTable строками, которые будем использовать для сравнения
    XmlNameTable xnt = new NameTable(); 
    ipKey            = xnt.Add("ip"); 
    httpMethodKey    = xnt.Add("http_method"); 
    fileKey          = xnt.Add("file");
    dateKey          = xnt.Add("date");
    referrerKey      = xnt.Add("referrer");
    
    //создаем XmlTextReader используя созданную XmlNameTable
    XmlTextReader xr = new XmlTextReader("logfile.xml", xnt);
    xr.WhitespaceHandling = WhitespaceHandling.Significant;
    XmlValidatingReader vr = new XmlValidatingReader(xr);
    vr.ValidationType = ValidationType.None;
    vr.EntityHandling = EntityHandling.ExpandEntities; 
    StreamWriter sw =  
      new StreamWriter ("logfile-archive.xml", false, Encoding.UTF8 ); 
    XmlWriter xw    = new XmlTextWriter (sw);                 
    
    vr.MoveToContent(); // Переходим к документу   
    xw.WriteStartElement(vr.Prefix, vr.LocalName, vr.NamespaceURI);
    WriteAttributes(vr, xw);    
     
    vr.Read(); // Переходим к первому элементу <event>
    // Записываем все события которые не от 127.0.0.1 (localhost) 
    do
    {
      ReadEvent(vr, out ip, out httpMethod, 
               out file, out date, out referrer);
      if(!ip.Equals("127.0.0.1")){
        WriteEvent(xw,ip, httpMethod, file, date, referrer); 
      }
      vr.Read(); //Переходим к следующему элементу <event> или к закрывающему тэгу <logfile>
    } while(vr.NodeType == XmlNodeType.Element);
     
    Console.WriteLine("Done");
    
    vr.Close();
    xw.Close();
  }
}
В итоге получаем следующий результат, записанный в файл 
logfile-archive.xml: <logfile>
 <event>
   <ip>192.168.0.1</ip>
   <http_method>POST</http_method>
    <file>comments.aspx</file>
    <date>1999-05-05T19:25:13.238220-08:00</date>
  </event>
</logfile>
Кроме того факта, что приведенный выше код использует связку 
XmlReader с XmlWriter, еще интересно отметить, 
что он использует NameTable для улучшения производительности 
сравнения текста при проверке имен тэгов элементов в методе 
ReadEvent(). Преимущества использования этого подхода для 
проверки имен тэгов элементов в XmlReader приведены в разделе 
Object Comparison Using XmlNameTable with XmlReader 
(http://msdn.microsoft.com/xml/default.aspx?pull=/library/en-us/cpguide/html/cpconObjectComparisonUsingXmlNameTableWithXmlReader.asp) 
документации MSDN. 
Благодарность Благодарю Мартина Гаджина (Martin Gudgin), который вдохновил меня на эту 
статью, предложив связывание XmlReader с 
XmlWriter в качестве решения проблемы редактирования больших 
XML-файлов журнала. Никакая часть настоящей статьи не может быть воспроизведена или 
передана в какой бы то ни было форме и какими бы то ни было средствами, будь то 
электронные или механические, если на то нет письменного разрешения владельцев 
авторских прав.
 Материал, изложенный в данной статье, многократно 
проверен. Но, поскольку вероятность технических ошибок все равно существует, 
сообщество не может гарантировать абсолютную точность и правильность приводимых 
сведений. В связи с этим сообщество не несет ответственности за возможные 
ошибки, связанные с использованием статьи.
 |  |  
		|  |  |  |  | 
	
 |