Простой разбор документов XML
На страницу 1, 2, 3 След.
|
Предыдущая тема :: Следующая тема |
Автор |
Сообщение |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Пн Сен 14, 2009 11:44  Простой разбор документов XML |
|
Полезность: 5
|
Может, кому пригодится.
Недавно столкнулся с проблемой разбора различных видов XML документов. После написания километров кода для разбора каждого XML документа в отдельности, пришел к мысли, что в таком темпе я буду долго писать обработку десятка XML документов. А если нужно будет добавить обработку еще двадцати? В общем, пришли к мысли, что проще XML выгрузить в PL/SQL таблицу и ее уже обрабатывать. Так оказалось гораздо проще.
Ниже приведен код пакета, который разбирает xml из строки и заполняет два массива (PL/SQL таблицы):
1. XML_values - содержит значения узлов.
Структура таблицы следующая:
self_id - Уникальный ID узла
name - Полное имя узла.
value - Значение узла.
2. XML_attributes - содержит значения атрибутов узлов.
self_id - Уникальный ID атрибута
collection_id - ID узла из таблицы XML_values.
name - Полное имя атрибута. Включает имя узла.
value - Значение атрибута. |
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Пн Сен 14, 2009 11:45   |
|
Полезность: Нет оценки
|
Глобальные описания:
Код: |
pragma macro(xml, '::[RUNTIME].[XML_DOM]');
type t_XML_record is record (
self_id number,
name varchar2(4000),
value varchar2(4000)
);
type t_XML_attr is record (
self_id number,
collection_id number,
name varchar2(4000),
value varchar2(4000)
);
type t_XML_values is table of t_XML_record; -- Тип Таблица значений тегов XML
type t_XML_attributes is table of t_XML_attr; -- Тип Таблица значений атрибутов XML
-- Процедура разбирает XML и возвращает таблицы значений тегов и атрибутов XML
procedure parse_buffer(doc in varchar2(32767), xml_values out t_XML_values, xml_attributes out t_XML_attributes);
-- Обход дерева XML
procedure parse(node in &xml.DOMNode, lvl in varchar2, xml_values in out t_XML_values, xml_attributes in out t_XML_attributes);
-- Возвращает значение для узла XML из таблицы xml_values
function get_node_value(node_name in varchar2(4000), xml_values in t_XML_values) return varchar2(4000);
|
|
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Пн Сен 14, 2009 11:46   |
|
Полезность: Нет оценки
|
Локальные описания:
Код: |
-- Возвращает значение для узла XML из таблицы xml_values
function get_node_value(node_name in varchar2(4000), xml_values in t_XML_values) return varchar2(4000) is
ret_val varchar2(4000);
begin
ret_val := '';
for i in 1..xml_values.count
loop
if upper(xml_values(i).name) = upper(node_name) then
ret_val := xml_values(i).value;
exit;
end if;
end loop;
return ret_val;
end;
-- Процедура разбирает XML и возвращает таблицы значений тегов и атрибутов XML
procedure parse_buffer(doc in varchar2(32767), xml_values out t_XML_values, xml_attributes out t_XML_attributes) is
xmldoc &xml.DOMDocument;
parser &xml.Parser;
p_node &xml.DOMNode;
child &xml.DOMNode;
begin
xml_values.delete;
xml_attributes.delete;
&xml.initialize;
parser := &xml.newParser;
&xml.parseBuffer(parser, doc);
xmldoc := &xml.getDocument(parser);
p_node := &xml.makeNode(xmldoc);
child := &xml.getFirstChild(p_node);
parse(child, '/', xml_values, xml_attributes);
-- Отладка - вывод содержимого таблиц
for i in 1..xml_values.count
loop
stdio.put_line_buf(xml_values(i).self_id||'. '||xml_values(i).name||' = '||xml_values(i).value);
end loop;
for i in 1..xml_attributes.count
loop
stdio.put_line_buf(xml_attributes(i).collection_id||'.'||xml_attributes(i).self_id||'. '||xml_attributes(i).name||' = '||xml_attributes(i).value);
end loop;
end;
-- Обход дерева XML
procedure parse(node in &xml.DOMNode, lvl in varchar2, xml_values in out t_XML_values, xml_attributes in out t_XML_attributes) is
cur_node &xml.DOMNode;
child_node &xml.DOMNode;
val_node &xml.DOMNode;
AttrMap &xml.DOMNamedNodeMap;
node_name varchar2(1000);
node_val varchar2(1000);
values_id number;
attr_id number;
begin
cur_node := node;
values_id := xml_values.count + 1;
while not &xml.isNull(cur_node)
loop
node_name := &xml.getNodeName(cur_node);
val_node := &xml.getFirstChild(cur_node);
node_val := replace(replace(&xml.getNodeValue(val_node), chr(10), ''), chr(13), '');
AttrMap := &xml.getAttributes(cur_node);
if node_name = '#text' then
cur_node := &xml.getNextSibling(cur_node);
continue;
end if;
xml_values(values_id).self_id := values_id;
xml_values(values_id).name := lvl||node_name||'/';
xml_values(values_id).value := node_val;
attr_id := xml_attributes.count + 1;
for i in 0..&xml.getLength(AttrMap) - 1
loop
xml_attributes(attr_id).self_id := attr_id;
xml_attributes(attr_id).collection_id := values_id;
xml_attributes(attr_id).name := lvl||node_name||'/'||&xml.getNodeName(&xml.item(AttrMap, i));
xml_attributes(attr_id).value := &xml.getNodeValue(&xml.item(AttrMap, i));
attr_id := attr_id + 1;
end loop;
child_node := &xml.getFirstChild(cur_node);
parse(child_node, lvl||node_name||'/', xml_values, xml_attributes);
cur_node := &xml.getNextSibling(cur_node);
values_id := xml_values.count + 1;
end loop;
end;
|
|
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Вт Сен 15, 2009 05:30   |
|
Полезность: Нет оценки
|
Пример XML:
Код: |
<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Record>
<FIO>Иванов Иван Иванович</FIO>
<SEX>M</SEX>
<PASNOM>11 22 333333</PASNOM>
<ACCOUNTS>
<ACCOUNT NO="22222222222222222222" A2TSTAT="1" A2CDESC="Депозит"/>
<ACCOUNT NO="40817810200000000079" A2TSTAT="3" A2CDESC="Текущий"/>
</ACCOUNTS>
</Record>
</Root>
|
Результат (содержимое PL/SQL таблиц)
Код: |
xml_values:
ID Name Value
--------------------------------------------------------
1 /Root/
2 /Root/Record/
3 /Root/Record/FIO/ Иванов Иван Иванович
4 /Root/Record/SEX/ M
5 /Root/Record/PASNOM/ 11 22 333333
6 /Root/Record/ACCOUNTS/
7 /Root/Record/ACCOUNTS/ACCOUNT/
8 /Root/Record/ACCOUNTS/ACCOUNT/
xml_attributes:
COLLECTION_ID ID Name Value
-------------------------------------------------------------------------------
7 1 /Root/Record/ACCOUNTS/ACCOUNT/A2CDESC Депозит
7 2 /Root/Record/ACCOUNTS/ACCOUNT/A2TSTAT 1
7 3 /Root/Record/ACCOUNTS/ACCOUNT/NO 22222222222222222222
8 4 /Root/Record/ACCOUNTS/ACCOUNT/A2CDESC Текущий
8 5 /Root/Record/ACCOUNTS/ACCOUNT/A2TSTAT 3
8 6 /Root/Record/ACCOUNTS/ACCOUNT/NO 40817810200000000079
|
|
|
 |
Volod Эксперт
Вступление в Клуб: 19.09.2007
|
Вт Сен 15, 2009 10:38   |
|
Полезность: Нет оценки
|
Тогда уж, чтобы циклы не крутить,
type t_XML_values is table of t_XML_record index by varchar2,
где index содержит name |
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Вт Сен 15, 2009 10:55   |
|
Полезность: Нет оценки
|
Volod пишет: | Тогда уж, чтобы циклы не крутить,
type t_XML_values is table of t_XML_record index by varchar2,
где index содержит name |
Тогда мы теряем однозначную связку узла с атрибутами. Так, в примере
есть два узла с именем /Root/Record/ACCOUNTS/ACCOUNT/. Чтобы получить атрибуты для конкретного узла ACCOUNT из xml_attributes необходимо однозначно идентифицировать этот узел. Для этого и служит self_id в xml_values - collection_id в xml_attributes равен self_id в xml_values. Например, для узла ACCOUNT с ID = 7 атрибут A2CDESC равен "Депозит". |
|
 |
Volod Эксперт
Вступление в Клуб: 19.09.2007
|
Вт Сен 15, 2009 11:07   |
|
Полезность: 1
|
И пусть служит self_id в xml_values , он же останется в таблице. Тут вопрос, наверное, в способе обработке xml_values. Либо один раз последовательно по узлам, либо поиск по избранным узлам. |
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Вт Сен 15, 2009 11:19   |
|
Полезность: Нет оценки
|
Volod пишет: | И пусть служит self_id в xml_values , он же останется в таблице. Тут вопрос, наверное, в способе обработке xml_values. Либо один раз последовательно по узлам, либо поиск по избранным узлам. |
Согласен |
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Ср Сен 30, 2009 08:29   |
|
Полезность: 1
|
Обновленная версия пакета.
Добавлена функция для разбора XML из файла.
Добавлено описание (в глобальных описаниях).
Исправлена ошибка преобразования XML с наличием тега xml-stylesheet.
Ниже находится текст пакета. |
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Ср Сен 30, 2009 08:30   |
|
Полезность: 1
|
Глобальные описания:
Код: |
pragma macro(xml, '::[RUNTIME].[XML_DOM]');
/* Автор: Корпушов Д.Н.
Описание:
Пакет предназначен для упрощения разбора XML.
Результат разбора XML помещается в две PL/SQL - таблицы (массива) типов t_XML_values и t_XML_attributes.
В таблицу t_XML_values помещаются значения тегов. Таблица имеет следующую структуру:
self_id - Уникальный ID тега. Генерируется автоматически.
name - Полное имя тега.
value - Значение тега.
В таблицу t_XML_attributes помещаются атрибуты тегов. Таблица имеет следующую структуру:
self_id - Уникальный ID атрибута. Генерируется автоматически.
collection_id - Ссылка на конкретный тег, к которому относится атрибут (содержит self_id из таблицы t_XML_values)
name - Полное имя атрибута. Включает в себя имя тега.
value - Значение атрибута.
Пакет имеет две интерфейсные функции:
parse_buffer(doc in varchar2(32767), xml_values out t_XML_values, xml_attributes out t_XML_attributes)
Разбирает XML из строки, переданной в параметре doc. Результат разбора помещается
в выходные параметры xml_values и xml_attributes.
parse_file(xml_file_name in varchar2(256), xml_values out t_XML_values, xml_attributes out t_XML_attributes)
Разбирает XML из файла. Полное имя файла передается в параметре xml_file_name. Результат разбора помещается
в выходные параметры xml_values и xml_attributes.
Пример вызова:
declare
xml_values [KOU_XML].t_XML_values;
xml_attributes [KOU_XML].t_XML_attributes;
xml_file_name varchar2(256);
begin
xml_file_name := './test.xml';
[KOU_XML].parse_file(xml_file_name, xml_values, xml_attributes);
stdio.put_line_buf('Массив xml_values');
for i in 1..xml_values.count
loop
stdio.put_line_buf(xml_values(i).self_id||'. '||xml_values(i).name||' = '||xml_values(i).value);
end loop;
stdio.put_line_buf('Массив xml_attributes');
for i in 1..xml_attributes.count
loop
stdio.put_line_buf(xml_attributes(i).collection_id||'.'||xml_attributes(i).self_id||'. '||xml_attributes(i).name||' = '||xml_attributes(i).value);
end loop;
end;
Пример результата работы.
XML:
<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Record>
<FIO>Иванов Иван Иванович</FIO>
<SEX>M</SEX>
<PASNOM>11 22 333333</PASNOM>
<ACCOUNTS>
<ACCOUNT NO="22222222222222222222" A2TSTAT="1" A2CDESC="Депозит"/>
<ACCOUNT NO="40817810200000000079" A2TSTAT="3" A2CDESC="Текущий"/>
</ACCOUNTS>
</Record>
</Root>
Результат разбора:
xml_values:
ID Name Value
--------------------------------------------------------
1 /Root/
2 /Root/Record/
3 /Root/Record/FIO/ Иванов Иван Иванович
4 /Root/Record/SEX/ M
5 /Root/Record/PASNOM/ 11 22 333333
6 /Root/Record/ACCOUNTS/
7 /Root/Record/ACCOUNTS/ACCOUNT/
8 /Root/Record/ACCOUNTS/ACCOUNT/
xml_attributes:
COLLECTION_ID ID Name Value
-------------------------------------------------------------------------------
7 1 /Root/Record/ACCOUNTS/ACCOUNT/A2CDESC Депозит
7 2 /Root/Record/ACCOUNTS/ACCOUNT/A2TSTAT 1
7 3 /Root/Record/ACCOUNTS/ACCOUNT/NO 22222222222222222222
8 4 /Root/Record/ACCOUNTS/ACCOUNT/A2CDESC Текущий
8 5 /Root/Record/ACCOUNTS/ACCOUNT/A2TSTAT 3
8 6 /Root/Record/ACCOUNTS/ACCOUNT/NO 40817810200000000079
*/
type t_XML_record is record (
self_id number,
name varchar2(4000),
value varchar2(4000)
);
type t_XML_attr is record (
self_id number,
collection_id number,
name varchar2(4000),
value varchar2(4000)
);
type t_XML_values is table of t_XML_record; -- Тип Таблица значений тегов XML
type t_XML_attributes is table of t_XML_attr; -- Тип Таблица значений атрибутов XML
-- Процедура разбирает XML из буфера и возвращает таблицы значений тегов и атрибутов XML
procedure parse_buffer(doc in varchar2(32767), xml_values out t_XML_values, xml_attributes out t_XML_attributes);
-- Процедура разбирает XML из файла и возвращает таблицы значений тегов и атрибутов XML
procedure parse_file(xml_file_name in varchar2(255), xml_values out t_XML_values, xml_attributes out t_XML_attributes);
-- Обход дерева XML
procedure parse(node in &xml.DOMNode, lvl in varchar2, xml_values in out t_XML_values, xml_attributes in out t_XML_attributes);
-- Возвращает значение для узла XML из таблицы xml_values
function get_node_value(node_name in varchar2(4000), xml_values in t_XML_values) return varchar2(4000);
|
|
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Ср Сен 30, 2009 08:32   |
|
Полезность: 1
|
Локальные описания:
Код: |
-- Возвращает значение для узла XML из таблицы xml_values
function get_node_value(node_name in varchar2(4000), xml_values in t_XML_values) return varchar2(4000) is
ret_val varchar2(4000);
begin
ret_val := '';
for i in 1..xml_values.count
loop
if upper(xml_values(i).name) = upper(node_name) then
ret_val := xml_values(i).value;
exit;
end if;
end loop;
return ret_val;
end;
-- Процедура разбирает XML из файла и возвращает таблицы значений тегов и атрибутов XML
procedure parse_file(xml_file_name in varchar2(255), xml_values out t_XML_values, xml_attributes out t_XML_attributes) is
xmldoc &xml.DOMDocument;
parser &xml.Parser;
p_node &xml.DOMNode;
child &xml.DOMNode;
begin
xml_values.delete;
xml_attributes.delete;
&xml.initialize;
parser := &xml.newParser;
&xml.parse(parser, xml_file_name);
xmldoc := &xml.getDocument(parser);
p_node := &xml.makeNode(xmldoc);
child := &xml.getFirstChild(p_node);
if lower(&xml.getNodeName(child)) = 'xml-stylesheet' then
child := &xml.getNextSibling(child);
end if;
parse(child, '/', xml_values, xml_attributes);
/*
-- Отладка - вывод содержимого таблиц
for i in 1..xml_values.count
loop
stdio.put_line_buf(xml_values(i).self_id||'. '||xml_values(i).name||' = '||xml_values(i).value);
end loop;
for i in 1..xml_attributes.count
loop
stdio.put_line_buf(xml_attributes(i).collection_id||'.'||xml_attributes(i).self_id||'. '||xml_attributes(i).name||' = '||xml_attributes(i).value);
end loop;
*/
end;
-- Процедура разбирает XML и возвращает таблицы значений тегов и атрибутов XML
procedure parse_buffer(doc in varchar2(32767), xml_values out t_XML_values, xml_attributes out t_XML_attributes) is
xmldoc &xml.DOMDocument;
parser &xml.Parser;
p_node &xml.DOMNode;
child &xml.DOMNode;
begin
xml_values.delete;
xml_attributes.delete;
&xml.initialize;
parser := &xml.newParser;
&xml.parseBuffer(parser, doc);
xmldoc := &xml.getDocument(parser);
p_node := &xml.makeNode(xmldoc);
child := &xml.getFirstChild(p_node);
if lower(&xml.getNodeName(child)) = 'xml-stylesheet' then
child := &xml.getNextSibling(child);
end if;
parse(child, '/', xml_values, xml_attributes);
/*
-- Отладка - вывод содержимого таблиц
for i in 1..xml_values.count
loop
stdio.put_line_buf(xml_values(i).self_id||'. '||xml_values(i).name||' = '||xml_values(i).value);
end loop;
for i in 1..xml_attributes.count
loop
stdio.put_line_buf(xml_attributes(i).collection_id||'.'||xml_attributes(i).self_id||'. '||xml_attributes(i).name||' = '||xml_attributes(i).value);
end loop;
*/
end;
-- Обход дерева XML
procedure parse(node in &xml.DOMNode, lvl in varchar2, xml_values in out t_XML_values, xml_attributes in out t_XML_attributes) is
cur_node &xml.DOMNode;
child_node &xml.DOMNode;
val_node &xml.DOMNode;
AttrMap &xml.DOMNamedNodeMap;
node_name varchar2(1000);
node_val varchar2(1000);
values_id number;
attr_id number;
begin
cur_node := node;
values_id := xml_values.count + 1;
while not &xml.isNull(cur_node)
loop
node_name := &xml.getNodeName(cur_node);
val_node := &xml.getFirstChild(cur_node);
node_val := replace(replace(&xml.getNodeValue(val_node), chr(10), ''), chr(13), '');
AttrMap := &xml.getAttributes(cur_node);
if node_name = '#text' then
cur_node := &xml.getNextSibling(cur_node);
continue;
end if;
xml_values(values_id).self_id := values_id;
xml_values(values_id).name := lvl||node_name||'/';
xml_values(values_id).value := node_val;
attr_id := xml_attributes.count + 1;
for i in 0..&xml.getLength(AttrMap) - 1
loop
xml_attributes(attr_id).self_id := attr_id;
xml_attributes(attr_id).collection_id := values_id;
xml_attributes(attr_id).name := lvl||node_name||'/'||&xml.getNodeName(&xml.item(AttrMap, i));
xml_attributes(attr_id).value := &xml.getNodeValue(&xml.item(AttrMap, i));
attr_id := attr_id + 1;
end loop;
child_node := &xml.getFirstChild(cur_node);
parse(child_node, lvl||node_name||'/', xml_values, xml_attributes);
cur_node := &xml.getNextSibling(cur_node);
values_id := xml_values.count + 1;
end loop;
end;
|
|
|
 |
dumpino Участник со стажем
Вступление в Клуб: 13.12.2011
|
Вт Фев 14, 2012 06:03   |
|
Полезность: Нет оценки
|
привет, друзья!
на дворе 2012 год, а последний пост по разбору XML файлов от 2009 года.
Я хотел узнать, какие сейчас следует использовать библиотеки для чтения/создания XML файлов (записей ожидается от 1 до 100, атрибутов тоже не много)?
пока то что я нарыл это: XML_DOM, XML, и TRANS_LIB_XML (в этом не уверен
Как вам описанный пример выше?
спасибо за любые наводки. |
|
 |
dnk_dz Эксперт
Вступление в Клуб: 19.09.2007
|
Вт Фев 14, 2012 07:18   |
|
Полезность: Нет оценки
|
dumpino пишет: |
на дворе 2012 год, а последний пост по разбору XML файлов от 2009 года. |
Боюсь ошибиться, но думаю, что особых изменений не произошло с тех пор, правда с дистрибутивом IBSO последние два года не работал.
Данный пакет был разработан с одной целью - упростить разбор множества xml-файлов для загрузки данных в IBSO с последующей их сложной обработкой, т.е. дает возможность разработчику быстро писать обработчики xml-файлов не вдаваясь в особенности муторного парсинга сложной структуры. В общем-то и xml ему знать не обязательно.
Как показала моя практика использования xml в IBSO, да и не только, - самый простой и эффективный способ создания xml - через обычную строковую переменную без использования парсера/модели. |
|
 |
maestro Профи
Вступление в Клуб: 12.10.2010
|
Вт Фев 14, 2012 08:18   |
|
Полезность: Нет оценки
|
dumpino пишет: | привет, друзья!
на дворе 2012 год, а последний пост по разбору XML файлов от 2009 года.
Я хотел узнать, какие сейчас следует использовать библиотеки для чтения/создания XML файлов (записей ожидается от 1 до 100, атрибутов тоже не много)?
пока то что я нарыл это: XML_DOM, XML, и TRANS_LIB_XML (в этом не уверен
Как вам описанный пример выше?
спасибо за любые наводки. |
Я пользуюсь стандартными Оракловыми XMLType и DBMS_XMLDOM. Постоянно использую XSLT-преобразование. В качестве отладчика XSLT - Altova XML spy.
Достоинства - быстро и удобно!
Недостатки: отсутствие поддержки со стороны PL+, приходится использовать PL/SQL вставки. Не понимаю почему ЦФТ использует какие-то левые библиотеки для парсинга XML, и не добавит в спецификацию PL+ обертку для стандартных оракловых конструкций. |
|
 |
maestro Профи
Вступление в Клуб: 12.10.2010
|
Вт Фев 14, 2012 08:29   |
|
Полезность: 1
|
dnk_dz пишет: | самый простой и эффективный способ создания xml - через обычную строковую переменную без использования парсера/модели. |
!! Не вздумайте так делать!!
Дело в том, что при создании XML стандартным парсером, выходной XML получается валидным! Разработчику нет нужды заморачиваться экранированием спецсимволов, вложенныхь XML-конструкций, кодировками и прочей херью, которая может не отвалидироваться на стороне приёмника. Стандартный парсер просто не даст Вам создать невалидный XML!
Собирать XML c F1 конкатенации в наше время - сродни каменному топору.
ИМХО, лучше потратить некоторое количество времени на изучение XML+XSD+XSLT, и затем эффективно пользоваться этими инструментами. Они дают возможность БЫСТРО создавать НАДЕЖНЫЕ приложения. |
|
 |
|
|
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах
|
|