(Окончание. Начало в №26)
XML - формат весьма распространённый и многогранный, а потому применяющийся в очень многих областях. Мы с вами уже успели рассмотреть примеры того, как считываются и записываются XML-данные в программах, написанных для платформы .NET. Мы также рассмотрели один из способов валидации данных и поговорили о том, что представляет собой XSL-преобразование. Сегодня мы завершим разговор о работе с XML в .NET-приложениях.
XSL: практика
Итак, XSL-документ мы с вами в прошлый раз сформировали. Теперь стоит, я так думаю, не растягивая дальше разговор о самом формате XSL-файлов, перейти уже к коду на C#, который будет осуществлять преобразование XML-файла, сформированного программой в первой части нашей статьи, в HTML-документ. Все комментарии и объяснения, как обычно, будут следовать сразу за программным кодом.
string XMLfile = "c:\\demo.xml"; string XSLfile = "c:\\demo.xsl"; string HTMLfile = "c:\\demo.html"; XPathDocument xpath = new XPathDocument(XMLfile); XslTransform xsl = new XslTransform(); XmlTextWriter xml = new XmlTextWriter(HTMLfile, System.Text.Encoding.Unicode); xml.Formatting = Formatting.Indented; xsl.Load(XSLfile); xsl.Transform(xpath, null, xml, null); xml.Close();
Итак, код написан, пришло время для разборок с ним. Первые три строки - это, как обычно, просто объявления путей к XML-документу, XSL-документу и получающемуся в итоге трансформации HTML-документу. Следующей строкой мы создаём экземпляр класса XPathDocument. Если верить MSDN'у, то этот класс "provides a fast, read-only cache for XML document processing using XSLT". То есть, по-русски говоря, он кэширует XML-документ для того, чтобы его потом можно было использовать при XSL-преобразовании. Затем мы создаём экземпляр класса XslTransform, который, собственно говоря, и будет у нас "рабочей лошадкой" во всех преобразованиях над XML-данными. Записываем итоговый HTML-файл мы, как вы можете заметить, с помощью класса XmlTextWriter, с использованием которого вы уже знакомы по первой части статьи. В общем-то, конечно, использование XmlTextWriter'а для записи HTML-документов может показаться несколько странным, но, если посмотреть внимательно, обнаружится, что HTML - это просто один из подвидов XML, просто в силу своей распространённости и исторических обстоятельств считающийся отдельным языком разметки. Мы указываем нашему XmlTextWriter'у использовать в качестве кодировки юникод, однако, конечно же, вы можете предложить ему использовать любую другую кодировку из того богатого арсенала, который поддерживает .NET Framework. Просто юникод на самом деле, на мой взгляд, является наилучшим и наиболее универсальным вариантом, и именно поэтому я сразу подталкиваю вас к его использованию. О том, чем так хорош юникод, можно прочитать в №5 "Компьютерных вестей" за 2008-й год.
Следующей строкой мы указываем XmlTextWriter'у писать HTML в человеческом виде, то есть с отступами и не всё в одну строку. Согласитесь, так всё-таки несколько приличнее. После этого загружаем в объект, отвечающий за XSL-преобразование, наш XSL-документ, и вызываем метод, ради которого, собственно говоря, и велись все предварительные приготовления - он непосредственно преобразует XML-данные в HTML-формат. В качестве аргументов метода Transform класса XslTransform используются (в порядке их следования в коде) XPath-объект, содержащий XML-данные; аргументы с namespace'ами для XSL (у нас этот аргумент, как видите, занулён); XmlTextWriter выходного потока данных; объект класса XmlResolver для обращения к URI внутри XSL-файла (см. предыдущую часть статьи, где приводится пример XSL-файла).
Написав этот код в редакторе, скомпилировав и запустив программу, мы получим... пшик. Потому что есть небольшая ошибочка в том самом XSL-файле, который мы разбирали в прошлой части статьи и к которому я вас только что отсылал. Если вы посмотрите на HTML-файл, получившийся в итоге работы нашей программы, то сможете заметить, что при XSL-преобразовании каким-то непонятным образом потерялись значения всех атрибутов в тегах нашего исходного документа. Дело в том, что точно так же потерялись "собаки" (символы @) перед названиями атрибутов в XSL-файле. Если вернуть их на место (то есть, обозначить все атрибуты так, как обозначен "attrib1"), то и трансформация волшебным образом исправится, и все значения атрибутов будут подставляться в HTML'ку.
Кстати, забыл сказать, что для работы кода, приведённого выше, потребуется добавить два новых namespace в C#-код: System.Xml.Xsl и System.Xml.XPath. Ну и, конечно, лучше обернуть этот код блоком try...catch на всякий пожарный. А то мало ли чего - сами понимаете.
Ещё немного о валидации
В прошлый раз мы рассмотрели использование класса XmlValidatingReader для работы со схемами XML-документов и для проверки самих документов на соответствие этим самым схемам. Однако открыв MSDN, я выяснил, что корпорация Microsoft по каким-то одной ей известным причинам посчитала, что этот способ недостаточно хорош для приложений, использующих новые версии .NET Framework'а, и заменила его другим. Теперь использовать XmlValidatingReader для проверки документов считается дурным тоном - вместо этого Microsoft рекомендует заниматься валидацией документов с использованием того же класса, который их читает в общем случае - то есть, XmlReader'а. В общем-то, нельзя сказать, чтобы это было нелогично, но это, тем не менее, вынуждает нас вернуться к вопросу о валидации XML-документов теми средствами, которые Microsoft предусмотрела во второй и более новых версиях .NET Framework'а. Поэтому сейчас мы рассмотрим код (из MSDN'а, так что на всякий случай напишу, что тот кусок кода, который следует ниже © 2008 Microsoft Corporation. All rights reserved).
public class Sample { public static void Main() { XmlReaderSettings settings = new XmlReaderSettings(); settings.ProhibitDtd = false; settings.ValidationType = ValidationType.DTD; settings.ValidationEventHandler += new Validation EventHandler (ValidationCallBack); XmlReader reader = XmlReader.Create("itemDTD.xml", settings); while (reader.Read()); } private static void ValidationCallBack(object sender, ValidationEventArgs e) { Console.WriteLine("Validation Error: {0}", e.Message); } }
Итак, здесь у нас, как видите, два метода. Main занимается как раз валидацией, а второй, ValidationCallBack - это просто обработчик события, возникающего при валидации (то есть, по-русски говоря, обработчик ошибок, поскольку других событий там вроде как особенно не наблюдается - на то она и валидация). При возникновении ошибок он будет выводить информацию о них на консоль.
Здесь для валидации используется, как я уже говорил, класс XmlReader. Для экземпляра этого класса мы должны специально указать настройки, чтобы он знал, что мы не просто почитываем XML в своё удовольствие, но ещё и занимаемся тяжёлым и кропотливым трудом, который у нас особенно востребован и почётен - мы проверяем. Для проверки мы используем DTD, так что мы должны разрешить использование DTD и указать в качестве типа валидации именно этот формат схемы документов. Затем, как видите, создаём экземпляр класса ValidationEventHandler, которому передаём ссылку на метод ValidationCallBack, о котором я уже рассказал выше, и присваиваем новый экземпляр класса в качестве обработчика событий нашего "читателя" XML. Ну а дальше создаём, собственно, сам XmlReader, передавая в конструкторе имя файла и настройки, и читаем, читаем, читаем...
К вопросу о том, как переделать всё так, чтобы работать не с DTD, а с XML Schema... А легко! Достаточно при создании экземпляра класса XmlReaderSettings воспользоваться следующим кодом (опять-таки, © 2008 Microsoft Corporation. All rights reserved.):
settings.ValidationType = ValidationType.Schema; settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema; settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
Здесь, как видите, есть нюансы, которые не позволяют нам ограничиться простым изменением типа валидации в опциях. Во-первых, мы должны указать обрабатывать вложенные схемы, а во-вторых, выводить предупреждения (они выводятся, когда что-то не так с самой схемой; если что-то не так с XML-файлом, то возникает ошибка).
Итоги
Итак, мы с вами завершили, по сути, разговор о том, как работать с XML-данными в .NET-приложениях. Ещё раз оговорюсь: пусть мы и говорили всё время о том, что данные считываются из файлов и записываются, опять-таки, тоже в файлы, но от этого ничего не менялось. XML-классы в Microsoft .NET Framework позволяют вам использовать любые потоки данных для чтения и записи, и вы можете скачивать XML-данные напрямую из Интернета или выводить сразу на принтер.
Как видите, репутация .NET как платформы, очень и очень дружественной к формату XML, подтвердилась - мы действительно ограничились платформенными классами и не особенно, говоря откровенно, напрягались, проводя валидацию или выполняя XSL-преобразования. Конечно, в реальных приложениях код будет куда сложнее, чем тот, который мы рассматривали во всех трёх частях статьи, однако основное мы рассмотрели, и именно эти действия над XML-данными будут основой того, что вы в дальнейшем будете реализовывать в своих приложениях. Надеюсь, информация вам пригодилась. Буду рад увидеть ваши мнения и комментарии на форуме "Компьютерных вестей" или в списке писем, пришедших на мой электронный почтовый ящик.
Вадим СТАНКЕВИЧ,
dreamdrusch@tut.by