Как использовать XQuery для преобразования последовательных тегов во вложенные теги или таблицу

У меня есть файл XML с последовательными тегами вместо вложенных тегов, например:

<title>
    <subtitle>
        <topic att="TopicTitle">Topic title 1</topic>
        <content att="TopicSubtitle">topic subtitle 1</content>
        <content att="Paragraph">paragraph text 1</content>
        <content att="Paragraph">paragraph text 2</content>
        <content att="TopicSubtitle">topic subtitle 2</content>
        <content att="Paragraph">paragraph text 1</content>
        <content att="Paragraph">paragraph text 2</content>

        <topic att="TopicTitle">Topic title 2</topic>
        <content att="TopicSubtitle">topic subtitle 1</content>
        <content att="Paragraph">paragraph text 1</content>
        <content att="Paragraph">paragraph text 2</content>
        <content att="TopicSubtitle">topic subtitle 2</content>
        <content att="Paragraph">paragraph text 1</content>
        <content att="Paragraph">paragraph text 2</content>
    </subtitle>
</title>

Я использую XQuery в BaseX и хочу преобразовать его в таблицу со следующими столбцами:

Title      Subtitle      TopicTitle      TopicSubtitle      Paragraph
Irrelevant Irrelevant    Topic title 1   Topic Subtitle 1   paragraph text 1
Irrelevant Irrelevant    Topic title 1   Topic Subtitle 1   paragraph text 2
Irrelevant Irrelevant    Topic title 1   Topic Subtitle 2   paragraph text 1
Irrelevant Irrelevant    Topic title 1   Topic Subtitle 2   paragraph text 2
Irrelevant Irrelevant    Topic title 2   Topic Subtitle 1   paragraph text 1
Irrelevant Irrelevant    Topic title 2   Topic Subtitle 1   paragraph text 2
Irrelevant Irrelevant    Topic title 2   Topic Subtitle 2   paragraph text 1
Irrelevant Irrelevant    Topic title 2   Topic Subtitle 2   paragraph text 2

Я новичок в XQuery и XPath, но уже понимаю основы навигации по узлам и выбора нужных. Чего я еще не знаю, так это того, как работать с последовательными данными, которые я хочу преобразовать во вложенный XML или таблицу (CSV?). Кто-нибудь может помочь?


person ChuyTM    schedule 11.10.2017    source источник


Ответы (2)


Вы можете преобразовать простой XML во вложенный, используя tumbling window (https://www.w3.org/TR/xquery-30/#id-windows), например.

for tumbling window $w in title/subtitle/*
    start $t when $t instance of element(topic)
return
    <topic
        title="{$t/@att}">
        {
            for tumbling window $content in tail($w)
                start $c when $c/@att = 'TopicSubtitle'
            return
                <subtopic
                    title="{$c/@att}">
                    {
                        tail($content) ! <para>{node()}</para>
                    }
                </subtopic>
        }
    </topic>

дает

<topic title="TopicTitle">
    <subtopic title="TopicSubtitle">
        <para>paragraph text 1</para>
        <para>paragraph text 2</para>
    </subtopic>
    <subtopic title="TopicSubtitle">
        <para>paragraph text 1</para>
        <para>paragraph text 2</para>
    </subtopic>
</topic><topic title="TopicTitle">
    <subtopic title="TopicSubtitle">
        <para>paragraph text 1</para>
        <para>paragraph text 2</para>
    </subtopic>
    <subtopic title="TopicSubtitle">
        <para>paragraph text 1</para>
        <para>paragraph text 2</para>
    </subtopic>
</topic>

Исходя из этого, я думаю, вы можете затем преобразовать все данные в данные, разделенные точкой с запятой, с помощью

string-join(
<title>
    <subtitle>
        {
            for tumbling window $w in title/subtitle/*
                start $t when $t instance of element(topic)
            return
                <topic
                    title="{$t/@att}"
                    value="{$t}">
                    {
                        for tumbling window $content in tail($w)
                            start $c when $c/@att = 'TopicSubtitle'
                        return
                            <subtopic
                                title="{$c/@att}"
                                value="{$c}">
                                {
                                    tail($content) ! <para>{node()}</para>
                                }
                            </subtopic>
                    }
                </topic>
        }
    </subtitle>
</title>//para ! string-join(ancestor-or-self::* ! (text(), @value, 'Irrelevant')[1], ';'), '&#10;')
person Martin Honnen    schedule 11.10.2017
comment
Это круто. Именно то, что мне было нужно. Изучив больше о падающем окне, я сомневаюсь, что смог бы найти его сам. Потребовалось немного, чтобы адаптировать его к имеющемуся у меня файлу, но теперь он работает с несколькими вложенными переворачивающимися окнами. Поскольку это кажется немного грязным, я хотел спросить, знаете ли вы лучший способ сделать это? Я имею в виду, лучше ли использовать Java, Python или другой язык для такого рода задач? Спасибо за помощь! - person ChuyTM; 12.10.2017
comment
Для меня, который в основном занимается XSLT (где вы могли бы использовать вложенные xsl:for-each-group group-starting-with), использование XQuery уже кажется грязным, но я думаю, что эти языки — хороший выбор для работы с XML. Если вы ищете что-то более структурированное для преобразования XML с помощью XQuery в CSV, проверьте github.com/CliffordAnderson/XQuery4Humanists/blob/master/. Что касается Python, я недостаточно хорошо знаю Python, и даже если бы я знал, я думаю, что это зависело бы от того, какой модуль вы можете установить. - person Martin Honnen; 12.10.2017
comment
Я думаю, что с чистой Java и встроенными классами XML потребуется много кода, я недостаточно хорошо знаю потоковую передачу/группировку Java 8, чтобы оценить объем кода, который потребуется там, - person Martin Honnen; 12.10.2017

Хотя позиционная группировка является наиболее общим подходом к такого рода проблемам (то есть переворачиванию окон в XQuery 3.0+, for-each-group/@group-starting-with в XSLT 2.0+, как описано Мартином Хонненом), я не думаю, что здесь это строго необходимо, потому что вы на самом деле не пытаясь использовать иерархическую структуру, заложенную в данных.

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

<table>{
    for $para in title/subtitle/content[@att='paragraph']
    return <row>
      <cell>irrelevant</cell>
      <cell>irrelevant</cell>
      <cell>{$para/preceding-sibling::topic[1]/string()}</cell>
      <cell>{$para/preceding-sibling::content[@att='TopicSubtitle'][1]/string()}</cell>
      <cell>{$para/string()}</cell>
    </row>
}</table>
person Michael Kay    schedule 12.10.2017