Рекурсивный цикл из xml в csv с помощью xmlstarlet

Я хотел бы преобразовать сложный xml в csv.

<tests>
    <test>
        <name>AAA</name> 
        <language>BBB</language> 
        <Project>
            <name>XXX</name>
            <id>123</id>
        </Project>
        <fac>
            <name>XXX</name>
            <idt>
                <number>99</number>
            <idt>
            <pers>YYY</pers>
        </fac>
        <fac>
            <name>BBB</name>
            <idt>
                <number>70</number>
            <idt>
            <pers>MMM</pers>
        </fac>
        <fac>
            <name>XXX</name>
            <idt>
                <number>40</number>
            <idt>
            <pers>XXX</pers>
        </fac>
        <date>2018</date>
    </test>
    <test>
    <name>BBB</name> 
    <language>CCC</language> 
    <Project>
        <name>AAA</name>
        <id>12</id>
    </Project>
    <fac>
        <name>YXX</name>
        <idt>
            <number>10</number>
        <idt>
        <pers>LLL</pers>
    </fac>
    <fac>
        <name>BB</name>
        <idt>
            <number>7</number>
        <idt>
        <pers>MM</pers>
    </fac>
    <fac>
        <name>XX</name>
        <idt>
            <number>40</number>
        <idt>
        <pers>XXX</pers>
    </fac>
    <date>2018</date>
</test>
 <tests> 

Что я сделал до сих пор:

xmlstarlet \
    sel -T -t -m /tests/test \
-v "concat(name,';'
,language,';'
,Project/name,';'
,Project/id,';'
,fac/name,';'
,fac/idt/number,';'
,fac/pers,';'
,date)"
   -n test.xml > test.csv

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

Для первого

    name;language;name;id;data contained in the first node"fac";date 
    name(same as first line);language(same as first line); etc..; data contained in the second "fac" node;date (same as first line)
etc... as much as there are face nodes

а затем для второго узла.

Я не знаю, можно ли это сделать с помощью xmlstarlet?

Заранее благодарю за помощь RFlow


person Rflow    schedule 23.08.2018    source источник
comment
Ваш пример ввода не является допустимым XML, похоже, что все ваши </idt> превратились в <idt>? После исправления ваш вызов xmlstarlet у меня работает нормально.   -  person npostavs    schedule 24.08.2018
comment
Спасибо, в моем xml этого не было, но в этом примере я забыл об этом. Даже если я изменю его, я получу только первый узел   -  person Rflow    schedule 24.08.2018
comment
Действительно? Я получаю две строки вывода: AAA;BBB;XXX;123;XXX;99;YYY;2018, за которыми следует BBB;CCC;AAA;12;YXX;10;LLL;2018.   -  person npostavs    schedule 24.08.2018
comment
Я не знаю, если бы я был понятен, я хочу, чтобы для первого «тестового» узла: AAA;BBB;XXX;123;XXX;99;YYY;2018, затем AAA;BBB;XXX;123;BBB;70;MMM;2018, затем AAA;BBB;XXX;123;XXX;40;XXX;2018 И то же самое для второго «тестового» узла.   -  person Rflow    schedule 24.08.2018
comment
Помог ли мой ответ или у вас все еще есть проблемы?   -  person Daniel Haley    schedule 28.08.2018
comment
@DanielHaley Спасибо за вашу помощь. Теперь все нормально :)   -  person Rflow    schedule 29.08.2018


Ответы (2)


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

Пример...

xmlstarlet sel -T -t -m "//fac" -v "concat(ancestor::test/name,';',ancestor::test/language,';',ancestor::test/Project/name,';',ancestor::test/Project/id,';',name,';',idt/number,';',pers,';',ancestor::test/date)" -n test.xml

Выход...

AAA;BBB;XXX;123;XXX;99;YYY;2018
AAA;BBB;XXX;123;BBB;70;MMM;2018
AAA;BBB;XXX;123;XXX;40;XXX;2018
BBB;CCC;AAA;12;YXX;10;LLL;2018
BBB;CCC;AAA;12;BB;7;MM;2018
BBB;CCC;AAA;12;XX;40;XXX;2018

Вот concat(), разбитый для удобства чтения...

concat(
    ancestor::test/name,';',
    ancestor::test/language,';',
    ancestor::test/Project/name,';',
    ancestor::test/Project/id,';',
    name,';',
    idt/number,';',
    pers,';',
    ancestor::test/date
)
person Daniel Haley    schedule 24.08.2018

Попробуйте выполнить xml linq:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string XML_FILENAME = @"c:\temp\test.xml";
        const string CSV_FILENAME = @"c:\temp\test.csv";
        static void Main(string[] args)
        {
            string[] headers = {
                                   "test name",
                                   "language", 
                                   "project name",
                                   "project id",
                                   "fac name",
                                   "idt number",
                                   "pers"
                               };
            StreamWriter writer = new StreamWriter(CSV_FILENAME);
            writer.WriteLine(string.Join(",", headers));

            XDocument doc = XDocument.Load(XML_FILENAME);

            foreach (XElement test in doc.Descendants("test"))
            {
                string testName = (string)test.Element("name");
                string language = (string)test.Element("language");

                XElement project = test.Element("Project");
                string projectName = (string)project.Element("name");
                string projectId = (string)project.Element("id");

                foreach(XElement fac in test.Elements("fac"))
                {
                    string facName = (string)fac.Element("name");
                    string number = (string)fac.Descendants("number").FirstOrDefault();
                    string pers = (string)fac.Element("pers");

                    string csvLine = string.Join(",", new string[] {
                        testName,
                        language,
                        projectName,
                        projectId,
                        facName,
                        number,
                        pers
                    });
                    writer.WriteLine(csvLine);
                }
            }


            writer.Flush();
            writer.Close();
        }
    }
}
person jdweng    schedule 23.08.2018
comment
Спасибо тоже попробую. - person Rflow; 29.08.2018