XML в CSV с помощью BaseX/XQuery

Я пытаюсь преобразовать большое количество xmls в один файл csv. Упрощенная структура xml будет выглядеть так:

<Receipts>
    <Receipt>
        <Field1 attribute1="a"/>
        <Fields2>
            <Field2 attribute2="1"/>
            <Field2 attribute2="2"/>
        </Fields2>
        <Field4 attribute4="4a"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="b"/>
        <Field4 attribute4="4b"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="c"/>
        <Fields2>
            <Field2 attribute2="3"/>
        </Fields2>
        <Field3 attribute3="c3"/>
        <Field4 attribute4="4c"/>
    </Receipt>
</Receipts>

И результат csv, который я хотел бы получить, это

Attribute1,Attribute2,Attribute3,Attribute4
a,1,,4a
a,2,,4a
b,,,4b
c,3,c3,4c

Я основывал свой код на этом ответе, но я могу либо иметь строку в csv для каждой квитанции со всеми объединенными атрибутами 2, либо возвращать только квитанции, которые имеют элемент Fields2 с полем 2, то есть: Либо это:

Attribute1,Attribute2,Attribute3,Attribute4
a,1 2,,4a
b,,,4b
c,3,c3,4c

Или это:

Attribute1,Attribute2,Attribute3,Attribute4
a,1,,4a
a,2,,4a
c,3,c3,4c

Мой код для первого случая:

declare option output:method "csv";
declare option output:csv "header=yes, separator=comma";

    declare context item := document {<Receipts>
    <Receipt>
        <Field1 attribute1="a"/>
        <Fields2>
            <Field2 attribute2="1"/>
            <Field2 attribute2="2"/>
        </Fields2>
        <Field4 attribute4="4a"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="b"/>
        <Field4 attribute4="4b"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="c"/>
        <Fields2>
            <Field2 attribute2="3"/>
        </Fields2>
        <Field3 attribute3="c3"/>
        <Field4 attribute4="4c"/>
    </Receipt>
</Receipts>};



for $x in //Receipt
return 
<csv>
  <record>
    <Attribute1>{$x/Field1/@attribute1/data()}</Attribute1>
    <Attribute2>{$x/Fields2/Field2/@attribute2/data()}</Attribute2>
    <Attribute3>{$x/Field3/@attribute3/data()}</Attribute3>
    <Attribute4>{$x/Field4/@attribute4/data()}</Attribute4>
  </record>
</csv>

А во втором случае это будет:

declare option output:method "csv";
declare option output:csv "header=yes, separator=comma";

    declare context item := document {<Receipts>
    <Receipt>
        <Field1 attribute1="a"/>
        <Fields2>
            <Field2 attribute2="1"/>
            <Field2 attribute2="2"/>
        </Fields2>
        <Field4 attribute4="4a"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="b"/>
        <Field4 attribute4="4b"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="c"/>
        <Fields2>
            <Field2 attribute2="3"/>
        </Fields2>
        <Field3 attribute3="c3"/>
        <Field4 attribute4="4c"/>
    </Receipt>
</Receipts>};



for $x in //Receipt for $y in $x/Fields2/Field2
return 
<csv>
  <record>
    <Attribute1>{$x/Field1/@attribute1/data()}</Attribute1>
    <Attribute2>{$y/@attribute2/data()}</Attribute2>
    <Attribute3>{$x/Field3/@attribute3/data()}</Attribute3>
    <Attribute4>{$x/Field4/@attribute4/data()}</Attribute4>
  </record>
</csv>

1 ответ

После более глубоких поисков я нашел решение. Во втором варианте во втором цикле for вы должны добавитьallowing empty функция, так что код в конечном итоге будет выглядеть так:

declare option output:method "csv";
declare option output:csv "header=yes, separator=comma";

    declare context item := document {<Receipts>
    <Receipt>
        <Field1 attribute1="a"/>
        <Fields2>
            <Field2 attribute2="1"/>
            <Field2 attribute2="2"/>
        </Fields2>
        <Field4 attribute4="4a"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="b"/>
        <Field4 attribute4="4b"/>
    </Receipt>
    <Receipt>
        <Field1 attribute1="c"/>
        <Fields2>
            <Field2 attribute2="3"/>
        </Fields2>
        <Field3 attribute3="c3"/>
        <Field4 attribute4="4c"/>
    </Receipt>
</Receipts>};



for $x in //Receipt for $y allowing empty in $x/Fields2/Field2
return 
<csv>
  <record>
    <Attribute1>{$x/Field1/@attribute1/data()}</Attribute1>
    <Attribute2>{$y/@attribute2/data()}</Attribute2>
    <Attribute3>{$x/Field3/@attribute3/data()}</Attribute3>
    <Attribute4>{$x/Field4/@attribute4/data()}</Attribute4>
  </record>
</csv>

Что возвращает Desider CSV:

Attribute1,Attribute2,Attribute3,Attribute4
a,1,,4a
a,2,,4a
b,,,4b
c,3,c3,4c
Другие вопросы по тегам