Java XML: преобразование структуры с выравниванием по глубине в структуру с выравниванием по ширине

Этот вопрос относится к алгоритму, который мы не смогли найти. Проблема заключается в следующем:

У нас есть XML (а точнее ODT-файл, содержащий content.xml), содержащий структуру с выравниванием по глубине:

<root xmlns:text="someuri">
<header>
...
</header>
<body>
    <text:span dns:att01="value">
        some text
    </text:span>
    <text:span dns:att02="value">
        more text
        <text:span dns:att03="value">
            even nested structures
        </text:span>
    </text:span>
</body>
</root>

Обратите внимание, что это упрощенный пример, содержащий только необходимые детали. Как вы можете видеть, это выглядит как "нормальная" структура xml с корнем, содержащим некоторые узлы текста и span. Для нашего приложения нам нужно сделать некоторую обработку. Поскольку все узлы диапазона содержат другие узлы, образующие древовидную структуру, целевой формат необходимо преобразовать, чтобы текстовые узлы были выровнены по ширине. Это желаемый формат:

<root xmlns:text="someuri">
<header>
...
</header>
<body>
    <text:marker-begin text:name="01" />
        some text
    <text:marker-end text:name="01" />
    <text:marker text:name="01" />

    <text:marker-begin text:name="02" />
        more text
        <text:marker-begin text:name="03" />
            even nested structures
        <text:marker-end text:name="03" />
        <text:marker text:name="03" />
    <text:marker-end text:name="02" />
    <text:marker text:name="02" />

</body>
</root>

Не позволяйте отступам раздражать вас, так как все текстовые узлы могут иметь прямого родителя, кроме узла body. Маркер используется для запуска определенной функции из стороннего программного обеспечения. Желаемые текстовые заметки теперь окружены пустыми элементами, обозначающими механизм маркировки. Теперь, после некоторой многословной подготовки, сам вопрос:

Как бы вы преобразовали структуру один в структуру два, используя механизмы DOM по умолчанию, доступные через Java. Это вообще возможно? Вы бы предпочли SAX-подход для сбора начальных и конечных элементов узла span? Существует ли алгоритм для этой проблемы? XLST невозможен из-за цепочки боковой обработки, которая должна быть сделана во время процесса.

1 ответ

Решение

Мы нашли решение с помощью подвоха:

у нас есть широкая реализация traverser (использование TreeWalker здесь не имеет никакого смысла) делегировать нужную операцию функции обработки:

// local field
Queue queue;

void traverse()
{
    queue = new LinkedListed();
    queue.add(documentRoot);

    queue.add(root);
    while (!queue.isEmpty()) 
    {
        current     = queue.poll();
        children    = current.getChildNodes();

        // the delegate
        process(current);

        for (int i = 0; i < children.getLength(); i++) 
        {
            child = children.item(i);
            switch(child.getNodeType())
            {
            case Node.ELEMENT_NODE:
            case Node.TEXT_NODE:
                queue.add(child);
                break;
            }
        } // end iteration over childnodes
    }
}

и вот функция обработки:

void process(Node node)
{
            String name                     = node.getNodeName();
        Map<String, String> attributes      = XMLUtil.extractAttributes(node);

        // this is basically the current node, but we need to save it as
        // extra reference to copy all child elements from it, to further process
        // the document tree
        Node target = null;
        Node next   = null;
        Node parent = node.getParentNode();

        if(name.equals("text:" + TARGET_ELEMENT)) {
            // deep copy
            target = node.cloneNode(true);

            // create the three relevant bookmark nodes
            Node bkMrkStart = document.createElement("bookmark-begin");
            Node bkMrkEnd   = document.createElement("bookmark-end");
            Node bkMrkRsd   = document.createElement("bookmark");

            // insert bookmark start
            node.getParentNode().insertBefore(bkMrkStart, node);

            // get next sibling or null, if last elment
            next = node.getNextSibling();

            // insert ending bookmark and 'residue'
            parent.insertBefore(bkMrkRsd, next);
            parent.insertBefore(bkMrkEnd, bkMrkRsd);

            // create new its tag element
            AuxiliaryElement nextAux = createAuxiliary(attributes);


            // apply generated id to created bookmarks
            XMLUtil.setAttribute(
                    "text:span", 
                    "id-[" + nextAux.getId().getString() + "]", 
                    bkMrkStart, bkMrkEnd, bkMrkRsd);


            NodeList children = target.getChildNodes();

            int index = 0;
            do
            {
                Node child = children.item(index).cloneNode(true);
                // it seems necessary to extra save this element
                            // for further processing
                queue.add(child);

                parent.insertBefore(child, bkMrkEnd);
            } while(++index < children.getLength());

            // delete old element
            parent.removeChild(node);

            // clear target
            target = null;
        }
}

Похоже, #removeChild или #insertBefore не отражается обходом. Возможно, это связано с нашей собственной реализацией первого обхода ширины. Однако использование этого подхода, как описано выше, дает желаемый результат.

Другие вопросы по тегам