Статьи

Вернуться к основам: XML в .NET

Одним из самых захватывающих последних достижений в области вычислительной техники был XML. Разработанный как более строгий и простой формат документа, чем SGML, XML теперь используется повсеместно для создания межплатформенных совместимых форматов файлов.

Это также ядро ​​.NET, и каждый разработчик .NET должен с этим справиться. Я старался сделать это введение в XML как можно более широким, поэтому оно должно быть полезным для пользователей всех убеждений в области развития.

XML 101: учиться ползать

Прежде чем мы рассмотрим особенности XML, важно знать, почему XML существует и где его можно использовать. Правильное понимание позволит вам эффективно использовать его в своих проектах.

Там, где HTML был разработан для отображения данных и определения того, как эти данные должны выглядеть, XML был разработан для описания и структурирования данных. Таким образом, сам файл XML фактически ничего не делает. Здесь не сказано, как отображать данные или что делать с данными, как в текстовом файле.

Но XML принципиально отличается от простого текста тем, что позволяет вам структурировать данные стандартным образом. Это важно — это означает, что другие системы могут интерпретировать ваш XML, что не так просто сделать простым текстом. Здесь описывается, что подразумевается под «совместимым форматом файла» — как только вы создадите файл XML, он будет открыт для всех. Входные данные и вся информация, необходимая для понимания структуры ваших данных, включены в файл.

Давайте возьмем пример. Вот текстовый файл и файл XML, в которых хранится одна и та же информация:

mymusic.txt  The Bends,Radiohead, Street Spirit  Is This It?,The Strokes, Last Nite   mymusic.xml  <catalog>  <cd>  <title>The Bends</title>  <artist>Radiohead</artist>  <tracks>  <track name="Street Spirit"/>  </tracks>  </cd>  <cd>  <title>Is This It?</title>  <artist>The Strokes</artist>  <tracks>  <track name="Last Nite"/>  </tracks>  </cd>  </catalog> 

Обратите внимание, как тема наших данных определяется в файле XML. Мы можем ясно видеть, что есть каталог, содержащий компакт-диски, каждый из которых содержит несколько треков (поклонники музыки заметят, что я сократил списки треков для места!). Вы также можете видеть, что XML может быть менее эффективным, чем некоторые другие форматы файлов. Тем не менее, во многих случаях потеря эффективности в результате увеличения размера может быть компенсирована скоростью обработки четко определенного XML-файла, поскольку анализаторы (программы, которые читают XML) могут предсказать структуру.

То, как мы будем интерпретировать простой текстовый файл, будет зависеть от того, как мы разработали наш собственный формат. Не существует никакой информации, которая могла бы рассказать другим, что означают реальные данные, их порядок или как их анализировать (читать) в других проектах. В отличие от этого, XML-файл четко показывает, что представляет каждая часть информации и где она принадлежит в иерархии данных. Эти «данные, описывающие данные» известны как метаданные и являются сильной стороной XML, поскольку вы можете создавать свои собственные спецификации и структурировать данные для интерпретации любой другой системой.

терминология

Чтобы начать эффективно использовать XML, необходимо получить глубокие знания его терминологии и структур файлов.

 <catalog>  <cd>    <title>The Bends</title>    <artist>Radiohead</artist>    <tracks>      <track name="Street Spirit"/>    </tracks>  </cd>  </catalog> 

<catalog>  <cd>    <title>The Bends</title>    <artist>Radiohead</artist>    <tracks>      <track name="Street Spirit"/>    </tracks>  </cd>  </catalog> 

XML-файлы являются иерархическими, каждый тег определяет элемент. Всем элементам требуется как открывающий, так и закрывающий тег ( <catalog> - открывающий тег, </catalog> - закрывающий тег). Некоторые элементы являются автономными и не требуют, чтобы какая-либо информация была вложена. Эти теги можно сделать самозакрывающимися, добавив "/>" в конец открывающего тега, как и в случае с элементом track выше.

Структура каталога такова, что он содержит компакт-диски, которые в свою очередь содержат дорожки. Это наша иерархия, и она будет важна позже, когда нам нужно будет проанализировать документ. Например, трек "Street Spirit" соответствует CD "The Bends", так же как трек "Last Nite" соответствует CD "Is This It?" Если бы мы не использовали подходящую иерархию, мы не смогли бы установить это во время анализа.

Иногда не имеет смысла появляться между открывающими и закрывающими тегами. Например, если нам нужно более одного фрагмента информации для описания элемента, мы можем включить эти несколько фрагментов информации в один тег. Поэтому мы определяем атрибуты элемента в форме attribute = "value".

После того как вы создали свой собственный набор элементов и структур, эти форматы можно назвать диалектами. Например, RSS - это диалект XML.

Пространства имен

С таким большим количеством различных диалектов, легко может возникнуть конфликт смысла. Например, возьмите следующие XML-файлы, которые описывают некоторые данные:

 <film-types>  <film-type>Action</film-type>  <film-type>Adventure</film-type>  </film-types> 
 <film-types>  <film-type>black and white</film-type>  <film-type>colour</film-type>  </film-types> 

Первый файл определяет жанры фильмов, а второй - различные типы пленок для камер. Но, как потребители этих файлов, как мы можем различать их?

Пространства имен дают ответ. Пространство имен XML позволяет нам квалифицировать элемент так же, как коды телефонных зон определяют номера телефонов. Там могут быть тысячи телефонных номеров 545-321. Когда мы добавляем код города и, возможно, международный код, мы делаем номер уникальным: +44 020 545-321.

«Код области» для пространств имен XML - это URI, связанный с префиксом для пространства имен. Мы определяем пространство имен с помощью объявления xmlns, за которым следует префикс, который равен URI, однозначно идентифицирующему пространство имен:

xmlns:movie="http://www.sitepoint.com/movies">

Добавив это определение пространства имен в качестве атрибута к тегу, мы можем использовать префикс фильма в этом теге и любые теги, которые он содержит, для полной квалификации наших элементов:

 <movie:film-types xmlns:movie="http://www.sitepoint.com/movies">  <movie:film-type>Action</movie:film-type>  <movie:film-type>Adventure</movie:film-type>  </movie:film-types> 

Аналогично, со вторым мы можем выбрать другое пространство имен «камера»:

 <camera:film-types xmlns:camera="http://www.sitepoint.com/camera">  <camera:film-type>black and white</camera:film-type>  <camera:film-type>colour</camera:film-type>  </camera:film-types> 

Теперь парсеры могут распознавать оба значения «типа фильма» и обрабатывать их соответственно.

Действительный XML

Чтобы XML-файл был действительным, он должен как минимум соответствовать спецификации XML версии 1.0. Это стандартизирует, как именно формируется ваш XML-файл, чтобы другие системы могли его понять. Например, XML 1.0 требует, чтобы все файлы XML состояли из одного корневого элемента; то есть один элемент содержит все остальные элементы. В приведенном выше примере с нашей музыкальной библиотекой каталог является нашим корневым элементом, поскольку он содержит все остальные наши элементы.

С полной спецификацией XML можно ознакомиться здесь , хотя, как мы вскоре увидим, .NET предоставляет вам инструменты для автоматической записи корректного XML.

XML-схемы

Хотя XML-файл может соответствовать спецификации XML, он может быть недопустимой формой определенного диалекта. Схема XML позволяет проверить наличие определенных элементов, а также убедиться, что представленные значения имеют правильный тип.

Существует несколько различных спецификаций схем: XSD, DTD и XSX. Хотя DTD (определение типа документа) является наиболее распространенной схемой, используемой в настоящее время, XSD (определение схемы XML) является более новым стандартом, который получает признание, поскольку он обеспечивает наилучший детальный контроль для проверки XML.

Поскольку XSD имеет много функций, это введение будет сосредоточено на некоторых простых функциях, которые вы сможете использовать.

Первая строка файла схемы обычно выглядит примерно так:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

Выше указано, что этот файл является схемой и что все элементы, которые мы собираемся использовать для проверки, принадлежат пространству имен схемы XML (которому мы назначаем префикс xs). В этом теге вы можете настроить дополнительные пространства имен и ряд параметров XSD. Проверьте полную спецификацию для получения дополнительной информации .

Одна из основных проверок, которую мы можем захотеть проверить, заключается в том, имеют ли элементы правильный тип (например, когда мы ожидаем число, мы не хотим получать строку текста). Эта проверка выполняется в XSD с использованием тега element:

 <xs:element name="foo" type="xs:integer"/> 

Это означает, что любые элементы с именем foo должны содержать целое число.

Следовательно, следующее подтвердит.

<foo>10</foo>

Тем не менее, приведенный ниже код не будет.

<foo>This is some text</foo>

Вы можете проверить диапазон различных типов; снова, смотрите спецификацию для более подробной информации.

Мы также можем проверить набор допустимых значений, используя перечислитель. Например, наш элемент foo может принимать только значения «яблоко», «апельсин» и «виноград». Здесь мы хотим определить наш собственный тип, так как это не просто строка, которую мы ищем. XSD предоставляет simpleType, чтобы позволить нам сделать это.

 <xs:element name="foo">  <xs:simpleType>    <xs:restriction base="xs:string">      <xs:enumeration value="Apple"/>      <xs:enumeration value="Orange"/>      <xs:enumeration value="Grape"/>    </xs:restriction>  </xs:simpleType>  </xs:element> 

<xs:element name="foo">  <xs:simpleType>    <xs:restriction base="xs:string">      <xs:enumeration value="Apple"/>      <xs:enumeration value="Orange"/>      <xs:enumeration value="Grape"/>    </xs:restriction>  </xs:simpleType>  </xs:element> 

Обратите внимание на element ограничения, который дает нам базовый тип для работы. Поскольку у нас есть список текстовых строк, он устанавливается как тип строки.

Итак, у нас есть базовая проверка значений для наших элементов, но как насчет атрибутов этих элементов? Ну, аналогичным образом, мы можем определить атрибуты внутри тега элемента:

 <xs:element name="foo">  <xs:attribute name="colour" type="xs:string"/>  </xs:element> 

Снова, атрибуты могут иметь свои собственные типы, используя элементы simpleType которые мы использовали для элемента выше.

По умолчанию все атрибуты обязательны. Однако, если мы не всегда требуем, чтобы атрибут был помещен в элемент, мы можем переопределить это значение по умолчанию, используя атрибут use="optional" в элементе attribute:

 <xs:attribute name="colour" type="xs:string" use="optional"/> 

Сложный элемент - это элемент, который содержит другие элементы, такие как элемент "CD" в приведенном ранее примере каталога:

 <cd>    <title>The Bends</title>    <artist>Radiohead</artist>    <tracks>      <track name="Street Spirit)"/>    </tracks>  </cd> 

Здесь мы должны убедиться, что элементы CD содержат заголовок, исполнителя и элемент треков. Мы делаем это, используя последовательность:

 <xs:element name="cd">  <xs:complexType>    <xs:sequence>      <xs:element name="title" type="xs:string"/>      <xs:element name="artist" type="xs:string"/>      <xs:element name="tracks" type="xs:string"/>    </xs:sequence>  </xs:complexType>  </xs:element> 

Мы можем создавать собственные типы или определять атрибуты внутри других элементов, чтобы помочь нам создать нашу иерархическую структуру.

Вы должны заметить, что «дорожки» сами по себе являются сложным типом, так как состоят из других элементов. Таким образом, нам нужно определить другой complexType , на этот раз внутри нашего элемента track. Наша схема будет выглядеть так:

 <xs:element name="cd">  <xs:complexType>    <xs:sequence>      <xs:element name="title" type="xs:string"/>      <xs:element name="artist" type="xs:string"/>      <xs:element name="tracks">        <xs:complexType>          <xs:sequence>            <xs:element name="track">              <xs:attribute name="name" type="xs:string"/>            </xs:element>          </xs:sequence>        </xs:complexType>      </xs:element>    </xs:sequence>  </xs:complexType>  </xs:element> 

Конечно, это только касается поверхности возможностей XSD, но должно дать вам хорошее понимание того, с чего начать писать свои собственные. С другой стороны, вы всегда можете обмануть, благодаря .NET и инструменту Microsoft XSD Inference , который создает файл схемы XSD с «наилучшим предположением» из любого данного XML-файла.

Учимся читать и писать

Прежде чем мы начнем заниматься более интересными аспектами XML, нам нужно знать, как создавать и использовать файлы XML в .NET. .NET организует свои XML-классы в пространстве имен System.Xml, поэтому вы можете использовать преимущества и ознакомиться с ключевыми классами, которые нам необходимо использовать:

  • XmlTextReader : класс XmlTextReader является лишь одним из методов чтения файлов XML. Он приближается к XML-файлу аналогично DataReader, так как вы шаг за шагом проходите по документу, принимая решения по ходу дела. Это самый простой класс для быстрого анализа XML-файла.
  • XmlTextWriter : аналогично, класс XmlTextWriter предоставляет возможность записи XML-файлов построчно.
  • XmlValidatingReader : XmlValidatingReader используется для проверки XML-файла по файлу схемы.
Чтение XML-файла в .NET

Давайте познакомимся с XmlTextReader . XmlTextReader основан на классе XmlReader , но был специально разработан для чтения потоков байтов, что делает его подходящим для файлов XML, которые должны быть расположены на диске, в сети или в потоке.

Как и в любом классе, первым шагом является создание нового экземпляра. Конструктор принимает местоположение файла XML, который он будет читать. Вот как это сделать в C #:

// file XmlTextReader reader = new XmlTextReader("foo.xml"); // url XmlTextReader reader = new XmlTextReader("http://www.sitepoint.com/foo.xml"); // stream (here, a StringReader s) XmlTextReader reader = new XmlTextReader(s);

После загрузки мы можем перемещаться по файлу только в прямом направлении. Это означает, что вам нужно структурировать свои процедуры синтаксического анализа так, чтобы они не зависели от порядка. Если вы не можете быть уверены в порядке элементов, ваш код должен быть в состоянии обработать любой порядок.

Мы перемещаемся по файлу, используя метод Read :

while (reader.Read()) { // parse our file }

Этот цикл будет продолжаться до тех пор, пока мы не достигнем конца нашего файла или формально не разорвем цикл. Нам нужно проверить каждый узел, определить его тип и получить необходимую нам информацию. Свойство NodeType предоставляет текущий тип читаемого узла, и здесь все становится немного сложнее!

XmlReader увидит следующий элемент как 3 разных узла:

<foo>text</foo>

Часть <foo> элемента распознается как узел XmlNodeType.Element . Текстовая часть распознается как узел XmlNodeType.Text , а закрывающий тег </foo> рассматривается как узел XmlNodeType.EndElement .

Приведенный ниже код показывает, как мы можем вывести тег XML через созданный ранее объект чтения:

while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: Console.Write("<"+reader.Name+">"); break; case XmlNodeType.Text: Console.Write(reader.Value); break; case XmlNodeType.EndElement: Console.Write("</"+reader.Name+">"); break; } }

Вот вывод этого кода:

 <foo>text</foo> 

Итак, что насчет атрибутов? Ну, они могут быть подобраны несколькими способами. Атрибут типа XmlNodeType.Attribute может быть проанализирован способом, показанным выше. Однако более элегантно, когда мы XmlNodeType.Element типа XmlNodeType.Element , мы можем перебирать атрибуты, используя XmlTextReader.MoveToNextAttribute :

case XmlNodeType.Element: Console.Write("<"+reader.Name); while (reader.MoveToNextAttribute()) { Console.WriteLine(reader.Name+" = "+reader.Value); } break;

Теперь, если мы будем кормить этим XML:

<foo first="1" second="2">text</foo>

мы получили бы этот вывод:

<foo first="1" second="2">text</foo>

Ранее мы игнорировали атрибуты и имели бы вывод:

<foo>text</foo>

Обратите внимание, что если элемент не содержит никаких атрибутов, цикл никогда не запускается. Это означает, что нам не нужно сначала проверять, есть ли у нас атрибуты. Тем не менее, количество атрибутов на узле можно узнать с помощью свойства AttributeCount.

Проверка XML в .NET

Помните эти файлы схемы? Используя класс XmlValidatingReader , мы можем легко проверить XML-файл по файлу схемы. Тем не менее, есть несколько других классов, которые мы должны использовать в первую очередь.

Мы можем указать объект XmlValidatingReader на файлы схемы, которые мы хотим использовать, заполнив XmlSchemaCollection нашими файлами схемы:

XmlSchemaCollection xsdCollection = new XmlSchemaCollection(); xsdCollection.Add("schemafile", "schema.xsd");

В этом примере мы используем схему, содержащуюся в файле schema.xsd.

Фактический класс XmlValidatingReader принимает XmlReader в своем конструкторе. Следовательно, нам нужно создать и заполнить XmlTextReader файлом XML, который мы хотим проверить:

 XmlTextReader reader;  XmlValidatingReader validatingReader;   reader = new XmlTextReader("foo.xml");  validatingReader = new XmlValidatingReader(reader); 

Затем мы добавляем нашу schemaCollection к схемам, которые мы хотим использовать читателю проверки:

validatingReader.Schemas.Add(xsdCollection);

Если валидатор обнаружит ошибку, событие будет запущено. Нам нужно перехватить это событие, создав обработчик события, и закодировать наш ответ на ошибки:

validatingReader.ValidationEventHandler += new ValidationEventHandler(validationCallBack); public void validationCallBack (object sender, ValidationEventArgs args) { Response.Write("Error:" + args.Message); }

Наконец, мы можем проверить наш файл, используя метод Read на проверяющем считывателе. На самом деле мы могли бы читать и обрабатывать узлы в файле, как мы делали это раньше, но для целей этого примера мы просто будем использовать пустой цикл while, чтобы пройти по файлу, проверяя его по ходу:

while (validatingReader.Read()){}

При обнаружении ошибок во время обработки файла будет сгенерировано событие, которое мы перехватываем и обрабатываем методом validationCallBack

Написание XML в .NET

Теперь, когда мы хорошо понимаем, как читать и проверять XML-файл, мы можем перейти к написанию XML.

Написание XML сделано очень безболезненно с XmlTextWriter класса XmlTextWriter . Опять же, используя прямой подход, мы можем создавать наши XML-файлы из разных типов узлов, которые выводятся по порядку.

Для начала нам нужно создать экземпляр класса:

XmlTextWriter writer = new XmlTextWriter("newfoo.xml", null);

Второй параметр, который здесь имеет значение null, позволяет нам указать формат кодировки, который будет использоваться в нашем XML-файле. При установке значения null создается стандартный XML-файл в кодировке UTF-8 без атрибута кодирования в элементе документа.

Теперь мы можем приступить к написанию элементов и атрибутов, используя наш код, состоящий из методов, представленных в XmlTextWriter . Давайте напишем наш предыдущий пример каталога CD:

 <catalog>  <cd>    <title>The Bends</title>    <artist>Radiohead</artist>    <tracks>      <track name="Street Spirit "/>    </tracks>  </cd>  </catalog> 

<catalog>  <cd>    <title>The Bends</title>    <artist>Radiohead</artist>    <tracks>      <track name="Street Spirit "/>    </tracks>  </cd>  </catalog> 

Наша первая строка - каталог элементов. Мы используем метод WriteStartElement чтобы написать это автору:

writer.WriteStartElement("catalog");

Обратите внимание, что мы не хотим закрывать этот элемент, пока не напишем наши элементы CD. Таким образом, следующий узел, который мы хотим добавить, является открывающим элементом CD:

writer.WriteStartElement("cd");

Теперь у нас есть элемент title ( <title>The Bends</title> ). Как и прежде, мы пишем начальный элемент, но этот тип будет отличаться, потому что у нас есть конкретное значение, которое мы хотим использовать. Поскольку у нас нет атрибутов в элементе title, мы можем использовать метод WriteElementString:

writer.WriteElementString("title", "The Bends"); writer.WriteElementString("artist", "Radiohead"); writer.WriteStartElement("tracks");

Теперь мы достигли элемента track с атрибутом name. Сначала нам нужно записать начало элемента, затем записать атрибуты и, наконец, закрыть наш элемент:

 writer.WriteStartElement("track");  writer.WriteAttributeString("name", "Street Spirit");  writer.WriteEndElement(); 

Обратите внимание, что нам не нужно говорить, какой элемент мы хотим закрыть, потому что автор автоматически напишет закрывающий тег для последнего открытого элемента, который в данном случае является track.

Теперь мы можем закрыть наши оставшиеся элементы таким же образом:

 writer.WriteEndElement();  writer.WriteEndElement(); 

Наконец, нам нужно «очистить» программу записи или, другими словами, вывести запрошенную информацию в наш XML-файл, а затем закрыть программу записи, чтобы освободить наш файл и ресурсы:

writer.Flush() writer.Close()

Резюме

На этом завершаем введение в XML и его использование в .NET. Надеюсь, мир XML больше не выглядит таким пугающим, как вы могли подумать, и теперь вы готовы использовать его возможности для вашего следующего проекта.