PowerShell: помещаем тело HTTP-ответа ЖЖ в хеш-таблицу (интерфейс «XML-RPC»)

Jan 30, 2023 06:35

Начало:
1. Общение с ЖЖ из скрипта по протоколу HTTP(S), ч.1: документация
2. Общение с ЖЖ из скрипта по протоколу HTTP(S), ч.2: URL-адрес входа, интерфейсы
3. Выбор веб-клиента для общения с ЖЖ из скрипта, PowerShell, моделирование
4. PowerShell: особенности использования командлета «Invoke-WebRequest»
5. Общение с ЖЖ из скрипта по протоколу HTTP(S), ч.3: первая успешная связь
6. PowerShell: помещаем тело HTTP-ответа ЖЖ в хеш-таблицу (интерфейс «flat»)

Окружение: операционная система «Windows 10», программа-оболочка «PowerShell» версии 7.

Напомню, в одном из предыдущих постов я получил по сети от веб-сервиса ЖЖ («LiveJournal») по протоколу общения с ЖЖ (через интерфейс «XML-RPC») на базе протокола HTTP(S) в теле HTTP-ответа строку в формате XML:

блок кода 1

PS C:\> $Response.Content

auth_schemec0server_time1674771448challengec0:1674770400:1048:60:yeM13Zf4UeujVPDIapTv:03d7b6a66990e95ba17ced533b9b98d2expire_time1674771508

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

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

Визуализация XML-дерева

С точки зрения программы строка, показанная в блоке кода выше, ясна и понятна. Поэтому в скрипте (программе) сразу можно приступить к обработке этой строки, то есть получению из нее хеш-таблицы, как мы это делали в предыдущем посте для строки, полученной по интерфейсу «flat». Но для того, чтобы объяснения в этом посте были понятны человеку, думаю, следует привести полученную строку в удобочитаемый для человека вид (в скрипте мы это делать не будем). Вот как это можно сделать с помощью одного из классов набора «System.Xml.Linq»:

блок кода 2

PS C:\> [System.Xml.Linq.XDocument]::Parse($Response.Content).ToString()

auth_scheme

c0

server_time

1674771448

challenge

c0:1674770400:1048...

expire_time

1674771508

Я заменил часть значения параметра «challenge» многоточием, чтобы код влез в формат поста. Думаю, понятно, что в программе-оболочке «PowerShell» это значение будет доступно полностью.

Люди, привыкшие работать с HTML-деревом или с XML-деревом, легко прочитают содержимое блока кода выше. Полученная визуализация XML-дерева помогает понять, как работать с этими данными дальше. Формат этого XML-дерева описан в спецификации протокола «XML-RPC».

Получение и исследование XML-дерева

Вернемся к строке в блоке кода 1. В терминах формата XML эта строка содержит XML-документ. Для работы с XML-документом в наборе классов «System.Xml» есть класс «System.Xml.XmlDocument». У названия этого класса есть псевдоним (alias) - «xml». Используя этот псевдоним, преобразуем нашу строку в объект класса «System.Xml.XmlDocument»:

PS C:\> $xml = [xml]$Response.Content

Теперь в переменной $xml содержится объект класса «System.Xml.XmlDocument» и к этой переменной мы можем применять методы данного класса, а также пользоваться свойствами, присущими объектам данного класса.

Кстати, при изучении объектов рекомендую пользоваться методом «GetType» класса «System.Object». Этот класс является корнем системы классов платформы «.NET», поэтому метод «GetType» наследуется всеми классами от корневого класса «System.Object». Например, этот метод можно использовать для изучения наших данных следующим образом:

PS C:\> ($Response.Content).GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object

PS C:\> $xml = [xml]$Response.Content
PS C:\> $xml.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False XmlDocument System.Xml.XmlNode

Из блока кода выше видно, как наша строка (объект класса «System.String») трансформировалась в объект класса «System.Xml.XmlDocument». Понимание того, какого типа (класса) у нас переменная, дает понимание того, какие методы и свойства можно использовать у данной переменной.

В строении полученного XML-документа, содержащего XML-дерево, можно разобраться следующим образом:

PS C:\> $xml

xml methodResponse
--- --------------
version="1.0" encoding="UTF-8" methodResponse

PS C:\> $xml.methodResponse

params
------
params

PS C:\> $xml.methodResponse.params

param
-----
param

И так далее, погружаясь всё глубже. На каждом шаге погружения можно проверять тип (класс) текущего объекта с помощью метода «GetType», как было показано ранее. После нескольких этапов этого погружения и проверки типов (классов) становится понятно, что XML-дерево состоит из XML-узлов разного типа (например, XML-элементы, текстовые узлы, узлы-комментарии и тому подобное, точно так же, как и у HTML-дерева). XML-узлы-дети записаны в свойства XML-узлов-родителей, у XML-узлов-детей есть свои XML-узлы-дети и так далее, так XML-дерево постепенно выстраивается целиком.

Конкретно для нашего XML-дерева объект в переменной $xml имеет класс «System.Xml.XmlDocument». XML-узлы methodResponse, params, param, value и struct имеют класс «System.Xml.XmlElement».

Тут всё понятно и логично. Но что происходит, когда у XML-узла-родителя есть несколько XML-узлов-детей с одним и тем же именем? В нашем случае, к примеру, у XML-узла struct имеется несколько XML-узлов-детей с одним и тем же именем member. В этом случае в свойство member записывается не один объект, а массив объектов, каждый из которых имеет класс «System.Xml.XmlElement».

Получение хеш-таблицы из XML-дерева в скрипте

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

function toHash($str) {
$xml = [xml]$str
$arr = $xml.methodResponse.params.param.value.struct.member
$hash = @{}
for ($i = 0; $i -lt $arr.Length; $i++) {
$hash[$arr[$i].name] = $arr[$i].value.FirstChild."#text"
}
return $hash
}

Эта функция во многом похожа на функцию, которую я написал в предыдущем посте для интерфейса «flat».

Конструкция $arr[$i].name, думаю, должна быть понятна из объяснений, данных выше; это название (name) параметра (member). А вот конструкция $arr[$i].value.FirstChild."#text", наверное, требует дополнительных пояснений.

Думаю, понятно, что $arr[$i].value - это значение (value) параметра (member). Но обратите внимание на XML-дерево, визуализированное в блоке кода 2 выше. Там видно, что у XML-узлов с названием value есть дополнительный XML-узел-ребенок, у которого может быть либо название int, либо название string. Если бы я осуществлял доступ к этому дополнительному XML-узлу-ребенку по его названию, то пришлось бы написать более сложное выражение с применением оператора ветвления if.

Ранее у меня был опыт работы с HTML-деревом, из которого я помнил, что у узла может присутствовать свойство для доступа к первому ребенку этого узла. Я поискал такое свойство в документации класса «System.Xml.XmlElement» и в этой документации такое свойство не нашел. Однако, я уже знал, что этот класс имеет следующую схему наследования:

Object -> XmlNode -> XmlLinkedNode -> XmlElement

Поэтому я поискал еще в документации к классам-предкам класса «System.Xml.XmlElement» и нужное свойство с названием «FirstChild» нашлось у класса «System.Xml.XmlNode», это свойство наследуется классом «System.Xml.XmlElement» и поэтому я могу его использовать в конструкции $arr[$i].value.FirstChild. Таким образом, я избегаю в скрипте лишнего ветвления и обращаюсь к XML-узлу-ребенку не по имени, а как к первому ребенку (в принципе, у XML-узла $arr[$i].value нашего XML-дерева есть только один ребенок).

У полученного таким образом XML-элемента есть свойство с названием #text, которое содержит нужное нам значение. Из-за того, что в названии свойства содержится специальный символ знака решетки #, пришлось взять название этого свойства в кавычки.

Проверим работу полученной функции:

PS C:\> toHash($Response.Content)

Name Value
---- -----
challenge c0:1674770400:1048...
auth_scheme c0
expire_time 1674771508
server_time 1674771448

Всё сработало успешно, хеш-таблица получена.

Продолжение следует.

Инструмент, Образование, Сайтостроение, Программирование, Английский язык

Previous post Next post
Up