0

这个问题是指我们无法找到的算法。问题如下:

我们有一个包含深度对齐结构的 XML(具体来说是一个包含 content.xml 的 ODT 文件):

<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 结构,其根包含一些文本和跨度节点。对于我们的应用程序,我们需要做一些处理。由于所有跨度节点都包含其他节点,形成树状结构,因此需要转换目标格式以使文本节点广度对齐。这是所需的格式:

<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>

不要让缩进激怒你,所有的文本节点都可能有一个直接的父节点,除了正文节点。标记用于触发第三方软件的某个功能。所需的文本注释现在被暗示标记机制的空元素包围。现在,经过一些冗长的准备,问题本身:

如何使用通过 java 提供的默认 DOM 机制将结构一转换为结构二。这甚至可能吗?您是否愿意建议一种 SAX 方法来收集跨度节点的开始和结束元素?这个问题是否已经存在算法?由于必须在过程中完成的副处理链,XLST 是不可能的。

4

1 回答 1

0

我们使用了一个肮脏的技巧找到了解决方案:

我们有一个遍历器的广度优先实现(在这里使用 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 没有被遍历反映。这可能是由于我们自己实现了广度优先遍历器。然而,使用如上所述的这种方法会产生所需的结果。

于 2013-05-27T13:47:31.620 回答