Есть например вот такой xml:
<?xml version="1.0" encoding="utf-8"?>
<main>
<fio n="attr">
<name>name1</name>
<pasp>
<pi>pi1</pi>
<pi>pi2</pi>
</pasp>
</fio>
<fio n="attr">
<name>name2</name>
</fio>
<sub_node>
<cr>
<val>val1</val>
<val>val2</val>
<extra>
<ex>ex1</ex>
<ex>ex2</ex>
</extra>
</cr>
<cr>
<val>val3</val>
<val>val4</val>
</cr>
</sub_node>
</main>
Мне нужно вывести определенные его элементы в плоскую таблицу по принципу "каждый с каждым". В mssql сервере я делаю это примерно так:
declare @sCmd nvarchar(max), @sFullPath varchar(max), @x xml
set @sFullPath = 'c:\in\test2.xml'
set @sCmd = N'select @xml = cast(t.data as xml) from OPENROWSET (BULK '+quotename(@sFullPath, N'''') + N', SINGLE_BLOB) t(data)'
exec sp_executesql @sCmd, N'@xml xml output', @x output;
select distinct
fio.n.value('name[1]', 'varchar(32)') 'fio.name'
, pi_.n.value('.', 'varchar(32)') 'pi_'
, val.n.value('.', 'varchar(5)') 'val'
, ex.n.value('.', 'varchar(10)') 'ex'
from @x.nodes('main') as main(n)
outer apply main.n.nodes('fio[@n="attr"]') as fio(n)
outer apply fio.n.nodes('pasp/pi') as pi_(n)
outer apply main.n.nodes('sub_node/cr') as cr(n)
outer apply cr.n.nodes('val') as val(n)
outer apply cr.n.nodes('extra/ex') as ex(n)
Ну и получаемый результат:
name1 pi1 val1 ex1
name1 pi1 val1 ex2
name1 pi1 val2 ex1
name1 pi1 val2 ex2
name1 pi1 val3 NULL
name1 pi1 val4 NULL
name1 pi2 val1 ex1
name1 pi2 val1 ex2
name1 pi2 val2 ex1
name1 pi2 val2 ex2
name1 pi2 val3 NULL
name1 pi2 val4 NULL
name2 NULL val1 ex1
name2 NULL val1 ex2
name2 NULL val2 ex1
name2 NULL val2 ex2
name2 NULL val3 NULL
name2 NULL val4 NULL
А вот теперь вопрос, как это можно сделать в любом алгоритмическом языке? Например vba/c++/java и пр. в которых есть циклы, рекурсия, ну и SelectNodes(), SelectSingleNode()..
Я как понимаю нужно реализовать рекурсивный обход дерева xml, но что-то никак не соображу как... Подскажите пожалуйста...
Надо с помощью XPath выбрать списки интересующих тебя узлов и по ним запустить нужное число вложенных циклов.
Здравствуйте, .alex, Вы писали:
A>Есть например вот такой xml:
A><skip>
A>Мне нужно вывести определенные его элементы в плоскую таблицу по принципу "каждый с каждым".
A><skip>
A>A>name1 pi1 val1 ex1
A>name1 pi1 val1 ex2
A>name1 pi1 val2 ex1
A>name1 pi1 val2 ex2
A><skip>
A>name2 NULL val3 NULL
A>name2 NULL val4 NULL
A>А вот теперь вопрос, как это можно сделать в любом алгоритмическом языке?
Звучит как задача для XSLT: описываем трансформацию, получаем выходные данные нужного формата. Кодировать это врукопашную — выглядит сомнительным.
Здравствуйте, .alex, Вы писали:
Типа такого XSLT-преобразования, выдаёт следующий результат (по образцу допилить колонки val и ext)
name1 pi1
name1 pi2
name2 NULL
Note: не забыть отредактировать строчку с комментарием, иначе будет строчка независимых символов, а нужен один символ переноса строки (глюк сайта, он не делает энкодинг для спецсимволов при рендеринге)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="/main/fio">
<xsl:variable name="my_name" select="name"/>
<xsl:choose>
<xsl:when test="pasp/pi">
<xsl:for-each select="pasp/pi">
<xsl:value-of select="concat($my_name, ' ')"/>
<xsl:value-of select="."/>
<xsl:text>& # 10;</xsl:text> <!-- тут убрать пробелы между символами, глюк RSDN -->
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($my_name, ' NULL')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Здравствуйте, .alex, Вы писали:
A>А вот нужно именно "врукопашную"... Не подскажете может где какие примеры поискать?
Ну вот следующий ответ от меня:
https://rsdn.org/forum/xml/8466126.1Автор: Mr.Delphist
Дата: 08.02.23
Рукопашным будет позвать этот XSLT, а остальное описывается трансформацией, Например, что-то типа такого:
https://learn.microsoft.com/en-us/dotnet/standard/linq/use-xslt-transform-xml-tree
string xslt = @"<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:template match='/Parent'>
<Root>
<C1>
<xsl:value-of select='Child1'/>
</C1>
<C2>
<xsl:value-of select='Child2'/>
</C2>
</Root>
</xsl:template>
</xsl:stylesheet>";
var oldDocument = new XDocument(
new XElement("Parent",
new XElement("Child1", "Child1 data"),
new XElement("Child2", "Child2 data")
)
);
var newDocument = new XDocument();
using (var stringReader = new StringReader(xslt))
{
using (XmlReader xsltReader = XmlReader.Create(stringReader))
{
var transformer = new XslCompiledTransform();
transformer.Load(xsltReader);
using (XmlReader oldDocumentReader = oldDocument.CreateReader())
{
using (XmlWriter newDocumentWriter = newDocument.CreateWriter())
{
transformer.Transform(oldDocumentReader, newDocumentWriter);
}
}
}
}
string result = newDocument.ToString();
Console.WriteLine(result);