Чтение XML
Перебор элементов XML-документа
Для чтения XML-документа предназначен экземпляр типа ЧтениеXml. В общем случае, читать XML-документ можно из экземпляра типа, производного от ПотокЧтения. Мы будем рассматривать чтение документа из файла. В качестве примера файла рассмотрим фрагмент манифеста универсального приложения ОС Windows. Это достаточно сложный XML-документ, который позволит увидеть основные особенности чтения таких документов.
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <Package IgnorableNamespaces="uap mp build" xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build"> <mp:PhoneIdentity PhoneProductId="A588A326-FD4F-441C-83B2-AA0B8554C548" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> <Properties> <DisplayName>ms-resource:AppName</DisplayName> <PublisherDisplayName>1C LLC</PublisherDisplayName> <Logo>icon_50x50.png</Logo> </Properties> <Resources> <Resource Language="EN-US"/> </Resources> <Capabilities> <uap:Capability Name="appointments"/> <uap:Capability Name="contacts"/> <Capability Name="internetClient"/> <Capability Name="privateNetworkClientServer"/> <Capability Name="internetClientServer"/> <uap:Capability Name="musicLibrary"/> <uap:Capability Name="picturesLibrary"/> <uap:Capability Name="removableStorage"/> <uap:Capability Name="videosLibrary"/> <DeviceCapability Name="location"/> <DeviceCapability Name="webcam"/> <DeviceCapability Name="microphone"/> <DeviceCapability Name="proximity"/> </Capabilities> <build:Metadata> <build:Item Name="cl.exe" Version="19.16.27030.1 built by: vcwrkspc"/> <build:Item Name="VisualStudio" Version="15.0"/> <build:Item Name="OperatingSystem" Version="6.3.9600.16384 (winblue_rtm.130821-1623)"/> <build:Item Name="Microsoft.Build.AppxPackage.dll" Version="15.0.28307.104"/> <build:Item Name="ProjectGUID" Value="{15630E4C-5D90-44AB-9CF0-FBC27700DDAA}"/> <build:Item Name="OptimizingToolset" Value="None"/> <build:Item Name="TargetRuntime" Value="Native"/> <build:Item Name="Microsoft.Windows.UI.Xaml.Build.Tasks.dll" Version="15.0.28307.102"/> <build:Item Name="WindowsMobile" Version="10.0.17763.0"/> <build:Item Name="MakePri.exe" Version="10.0.17763.132 (WinBuild.160101.0800)"/> </build:Metadata> </Package>
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.Следующий()
Консоль.Записать("Имя узла - " + Чтение.Имя)
;
;
Вызов метода Следующий() приводит к последовательному обходу всех элементов читаемого XML-документа. Когда данный метод вернет значение Ложь, это будет означать, что файл завершился. Результат исполнения этого программного кода будет не очень понятным (приведен фрагмент):
Имя узла - Package
Имя узла - mp:PhoneIdentity
Имя узла - mp:PhoneIdentity
Имя узла - Properties
Имя узла - DisplayName
Имя узла -
Имя узла - DisplayName
Имя узла - PublisherDisplayName
Имя узла -
Имя узла - PublisherDisplayName
Имя узла - Logo
Имя узла -
Имя узла - Logo
Имя узла - Properties
...
Первое, что смущает при взгляде на результат работы программы — некоторые элементы выводятся один раз, некоторые — два раза, а некоторые элементы вообще не имеют имени. Попробуем разобраться с этим вопросом.
Каждый элемент XML имеет открывающий и закрывающий тег. При этом оба этих тега в теле
документа имеют одинаковое имя:
<tag>
текст</tag>
. Закрывающий тег
несколько отличается, но это нам сейчас не интересно. Экземпляр
ЧтениеXml читает наш документ последовательно. При чтении
отдельно читается начало элемента и отдельно — его окончание. А так как имена в
открывающем и закрывающем тегах одинаковые — мы видим две строки с одним именем.
Соответственно, если строка с именем только одна — скорее всего, до закрывающего
тега еще не дошли.
Чтобы определить, какой элемент в данный момент считан из нашего документа, тип ЧтениеXml предоставляет свойство ВидУзла. С помощью этого свойства мы может проверить наше предыдущее утверждение:
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.Следующий()
Консоль.Записать("Имя узла - " + Чтение.Имя + ", тип - " + Чтение.ВидУзла)
;
;
Результат работы нашего примера будет следующим (приведен фрагмент того же размера):
Имя узла - Package, тип - StartElement
Имя узла - mp:PhoneIdentity, тип - StartElement
Имя узла - mp:PhoneIdentity, тип - EndElement
Имя узла - Properties, тип - StartElement
Имя узла - DisplayName, тип - StartElement
Имя узла - , тип - Text
Имя узла - DisplayName, тип - EndElement
Имя узла - PublisherDisplayName, тип - StartElement
Имя узла - , тип - Text
Имя узла - PublisherDisplayName, тип - EndElement
Имя узла - Logo, тип - StartElement
Имя узла - , тип - Text
Имя узла - Logo, тип - EndElement
Имя узла - Properties, тип - EndElement
...
StartElement
— таким образом описывается начало элемента.EndElement
— таким образом описывается окончание элемента.Text
— таким типом отмечается содержимое узла XML-документа, если это содержимое не является другим элементом.
<tag>
содержимое</tag>
В
то же время не все узлы такое содержимое имеют. Для того чтобы определить, имеет
узел содержимое или нет, предназначено свойство
ЧтениеXml.ИмеетЗначение.Наш пример, модифицированный для того, чтобы выводить содержимое для элементов, которые его содержат, выглядит следующим образом:
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.Следующий()
Консоль.Записать("Имя узла - " + Чтение.Имя + ", тип - " + Чтение.ВидУзла)
если Чтение.ИмеетЗначение
Консоль.Записать("Содержимое: " + Чтение.Значение)
;
;
;
Результат работы этой программы будет следующим:
Имя узла - Package, тип - StartElement
Имя узла - mp:PhoneIdentity, тип - StartElement
Имя узла - mp:PhoneIdentity, тип - EndElement
Имя узла - Properties, тип - StartElement
Имя узла - DisplayName, тип - StartElement
Имя узла - , тип - Text
Содержимое: ms-resource:AppName
Имя узла - DisplayName, тип - EndElement
Имя узла - PublisherDisplayName, тип - StartElement
Имя узла - , тип - Text
Содержимое: 1C LLC
Имя узла - PublisherDisplayName, тип - EndElement
Имя узла - Logo, тип - StartElement
Имя узла - , тип - Text
Содержимое: icon_50x50.png
Имя узла - Logo, тип - EndElement
Имя узла - Properties, тип - EndElement
...
Кроме безусловного чтения следующего элемента, с помощью экземпляра
ЧтениеXml можно читать элементы какого-то заранее известного
имени. Чтение будет выполняться без учета иерархии элементов. Для того, чтобы
выполнить такое чтение, необходимо использовать метод
СледующийДо(). Параметром метода является имя элемента,
начало которого необходимо прочитать. После того как выполнено позиционирование на
требуемый элемент, необходимо вызвать метод Следующий(), а затем
снова вызвать метод СледующийДо(). В примере будет
демонстрироваться чтение элементов с именем Capability
. Всего их
три.
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.СледующийДо("Capability")
Консоль.Записать("Имя узла - " + Чтение.Имя + ", тип - " + Чтение.ВидУзла)
если Чтение.ИмеетЗначение
Консоль.Записать("Содержимое: " + Чтение.Значение)
;
Чтение.Следующий()
;
;
В том случае, если какой-либо узел нам не нужен, мы можем пропустить всё его (узла)
содержимое и возобновить чтение с узла, который следует в нашем XML-документе после
пропускаемого узла. Узел пропускается полностью, включая все вложенные узлы, если
таковые имеются. Для того чтобы пропустить узел, необходимо использовать метод
Пропустить(). В следующем примере мы дойдем до узла
<Capabilities>
, затем пропустим его и сразу окажемся на
стартовом элементе узла <build:Metadata>
:
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.Следующий()
если Чтение.ВидУзла == ВидУзлаXml.НачалоЭлемента
Консоль.Записать("Имя узла - " + Чтение.Имя + ", тип - " + Чтение.ВидУзла)
если Чтение.Имя == "Capabilities"
Консоль.Записать("Пропускаем этот узел!")
Чтение.Пропустить()
;
;
;
;
Также при переборе элементов полезным может оказаться метод ЭтоПустойЭлемент(), который позволяет понять, что у
текущего узла нет значения. Другими словами, метод ЭтоПустойЭлемент() вернет значение Истина для
узла вида <tag/>
.
Чтение атрибутов узла XML-документа как данных различных типов
- КоличествоАтрибутов() — позволяет получить количество атрибутов у данного элемента.
- ИмяАтрибута() — позволяет получить имя атрибута по индексу.
- ЗначениеАтрибута() — позволяет получить значение атрибута по имени атрибута.
- ЗначениеАтрибутаПоИндексу() — позволяет получить значение атрибута по индексу атрибута.
Работать с атрибутами можно только для такого узла, который является началом элемента. Расширим пример чтения XML-документа кодом, который будет отображать атрибуты каждого узла (если атрибуты присутствуют). Код будет иметь следующий вид:
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.Следующий()
Консоль.Записать("Имя узла - " + Чтение.Имя + ", тип - " + Чтение.ВидУзла)
если Чтение.ИмеетЗначение
Консоль.Записать("Содержимое: " + Чтение.Значение)
;
если Чтение.ВидУзла != ВидУзлаXml.НачалоЭлемента
продолжить
;
для индекс = 0 по Чтение.КоличествоАтрибутов()-1
пер ИмяАтрибута = Чтение.ИмяАтрибута(индекс)
Консоль.Записать("\тАтрибут: " + ИмяАтрибута + " = " + Чтение.ЗначениеАтрибута(ИмяАтрибута))
;
;
;
Результат работы этого примера будет следующим:
Имя узла - Package, тип - StartElement
Атрибут: IgnorableNamespaces = uap mp build
Имя узла - mp:PhoneIdentity, тип - StartElement
Атрибут: PhoneProductId = A588A326-FD4F-441C-83B2-AA0B8554C548
Атрибут: PhonePublisherId = 00000000-0000-0000-0000-000000000000
Имя узла - mp:PhoneIdentity, тип - EndElement
Имя узла - Properties, тип - StartElement
Имя узла - DisplayName, тип - StartElement
Имя узла - , тип - Text
Содержимое: ms-resource:AppName
Имя узла - DisplayName, тип - EndElement
Имя узла - PublisherDisplayName, тип - StartElement
Имя узла - , тип - Text
Содержимое: 1C LLC
Имя узла - PublisherDisplayName, тип - EndElement
Имя узла - Logo, тип - StartElement
Имя узла - , тип - Text
Содержимое: icon_50x50.png
Имя узла - Logo, тип - EndElement
Имя узла - Properties, тип - EndElement
...
- Ууид / Число / Булево
- Байты / БайтыBase64
- Время / ДатаВремя / Дата / Длительность / Момент
Все эти методы начинаются с префикса ЗначениеАтрибутаКак. Например, для того чтобы прочитать атрибут элемента как Число, следует использовать метод:
ЗначениеАтрибутаКакЧисло(Имя: Строка, ПространствоИмен: Строка? = Неопределено): Число
Чтение содержимого узла XML-документа как данных различных типов
Для чтения содержимого элемента предназначены свойства типа ЧтениеXml ИмеетЗначение и Значение. Первое свойство позволяет узнать, что элемент имеет содержимое, а второе свойство позволяет его считать. Однако значение возвращается исключительно в текстовом виде. Это не всегда бывает удобно. Зачастую может быть известно заранее, что содержимое того или иного элемента XML-документа имеет конкретный тип. Тип ЧтениеXml обладает методами, которые позволяют считывать значения некоторых типов. Все эти методы начинаются с префикса ПрочитатьСодержимоеКак:
- ПрочитатьСодержимоеКакБайты()
- Читает содержимое узла как шестнадцатеричное число и преобразует к значению типа Байты
- ПрочитатьСодержимоеКакБайтыBase64()
- Читает содержимое узла как число в записи Base64 и преобразует к значению типа Байты
- ПрочитатьСодержимоеКакБулево()
- Данный метод считывает значение узла и преобразует его к типу
Булево:
- Текст true и 1 преобразуются в значение Истина
- Текст false и 0 преобразуются в значение Ложь
- ПрочитатьСодержимоеКакВремя()
- Читает содержимое узла и преобразует к значению типа Время
- ПрочитатьСодержимоеКакДатаВремя()
- Читает содержимое узла и преобразует к значению типа ДатаВремя
- ПрочитатьСодержимоеКакДату()
- Читает содержимое узла и преобразует к значению типа Дата
- ПрочитатьСодержимоеКакДлительность()
- Читает содержимое узла и преобразует к значению типа Длительность
- ПрочитатьСодержимоеКакМомент(ЧасовойПояс = ЧасовойПояс.Текущий())
- Читает содержимое узла и преобразует к значению типа Момент. Если у содержимого узла не указан часовой пояс, для преобразования будет использован часовой пояс, указанный в аргументе ЧасовойПояс
- ПрочитатьСодержимоеКакУуид()
- Читает содержимое узла и преобразует к значению типа Ууид
- ПрочитатьСодержимоеКакЧисло()
- Читает содержимое узла и преобразует к типу Число
Пространства имен
Имена элементов и атрибутов в XML-документ могут быть квалифицированными и неквалифицированными (локальными). Квалифицированное имя уникально в рамках XML-документа. Квалифицированное имя состоит из префикса, определяющего пространство имен, и локального имени. Префикс и локальное имя разделяются символом двоеточия (":"). Когда XML-документ читается с помощью типа ЧтениеXml, то программист может получить доступ к любой части имени элемента или атрибута:
- Для элемента:
- Свойство Имя. Содержит квалифицированное имя текущего узла. Квалифицированное имя состоит из префикса и локального имени (разделенного символом двоеточия).
- Свойство Префикс. Содержит префикс пространства имен для текущего узла.
- Свойство ЛокальноеИмя. Содержит локальное имя текущего узла документа.
- Свойство ПространствоИмен. Данное свойство позволяет получить URI пространства имен. Префикс этого пространства имен можно получить с помощью свойства Префикс.
- Для атрибута:
- Метод ИмяАтрибута(Индекс: Число). Возвращает квалифицированное имя атрибута по индексу Индекс. Квалифицированное имя состоит из префикса и локального имени (разделенного символом двоеточия).
- Метод ПрефиксАтрибута(Индекс: Число). Возвращает префикс пространства имен для атрибута с указанным индексом.
- Метод ЛокальноеИмяАтрибута(Индекс: Число). Возвращает локальное имя атрибута по индексу Индекс.
- Метод ПространствоИменАтрибута(Индекс: Число). Данный метод позволяет получить URI пространства имен атрибута по индексу Индекс. Префикс пространства имен атрибута можно получить с помощью метода ПрефиксАтрибута(Индекс: Число).
Далее приведен пример программного кода, который отображает всю информацию об узлах и атрибутах XML-документа, связанную с пространствами имен. Будут выведены квалифицированное имя, префикс имени, локальное имя и URI пространства имен.
метод Скрипт()
пер ВременныйКаталог = СредаИсполнения.ПолучитьПеременную("temp")
пер Файл = новый Файл(ВременныйКаталог + "\\manifest.xml")
пер Чтение = новый ЧтениеXml(Файл.ОткрытьПотокЧтения())
пока Чтение.Следующий()
если Чтение.ВидУзла == ВидУзлаXml.НачалоЭлемента
Консоль.Записать("Имя узла: " + Чтение.Имя)
Консоль.Записать("\tПрефикс : " + Чтение.Префикс)
Консоль.Записать("\tЛокальное имя : " + Чтение.ЛокальноеИмя)
Консоль.Записать("\tURI пространства имен: " + Чтение.ПространствоИмен)
для индекс = 0 по Чтение.КоличествоАтрибутов()-1
пер ИмяАтрибута = Чтение.ИмяАтрибута(индекс)
Консоль.Записать("\т\тИмя атрибута: " + ИмяАтрибута)
Консоль.Записать("\т\т\тПрефикс : " + Чтение.ПрефиксАтрибута(индекс))
Консоль.Записать("\т\т\тЛокальное имя : " + Чтение.ЛокальноеИмяАтрибута(индекс))
Консоль.Записать("\т\т\тURI пространства имен: " + Чтение.ПространствоИменАтрибута(индекс))
;
;
;
;