一、场景介绍

后台有接口提供类似以下的xml数据(部分数据省略):

<graph>
    <root>
        <node id="nodeA" property="value">
            <name>Node A</name>
        </node>
        <node id="nodeB" property="value">
            <name>Node B</name>
        </node>
        <node id="nodeC" property="value">
            <name>Node C</name>
        </node>
        <link id="linkAC" source="nodeA" target="nodeC"/>
        <link id="linkBC" source="nodeB" target="nodeC"/>
    </root>
</graph>

发送到前端后,在前端渲染为svg格式的图形(已流转的线被设置为红色),如下:

<svg width="400" height="240" viewBox="0 0 400 240">
    <rect id="nodeA" x="40" y="25" width="80" height="50" rx="5" ry="5" style="fill:none;stroke:#4da4fb;stroke-width:2;"/>
    <text x="50" y="55" fill="black">Node A</text>
    <rect id="nodeB" x="280" y="25" width="80" height="50" rx="5" ry="5" style="fill:none;stroke:#4da4fb;stroke-width:2;"/>
    <text x="292" y="55" fill="black">Node B</text>
    <rect id="nodeC" x="160" y="180" width="80" height="50" rx="5" ry="5" style="fill:none;stroke:#4da4fb;stroke-width:2;"/>
    <text x="173" y="212" fill="black">Node C</text>
    <polyline id="linkAC" points="120,50 200,50 200,180" style="fill:none;stroke:red;stroke-width:2;"/>
    <polyline id="linkBC" points="280,50 200,50 200,180" style="fill:none;stroke:black;stroke-width:2;"/>
</svg>

二、问题

如上图所示,由于此图形是先画了A与C之间的连线,然后才画的B与C之间的连线,因此在图形中看到的是连线BC与连线AC重合的一段是显示连线BC的颜色,但实际是需要显示完整的当前流转的线(例如:AC之间的线应该全为红色)。

三、解决方法

为了解决上述问题,特意看了下前端的解析过程:是按xml节点的顺序解析,只要保证解析线时它的source和target已被解析就行。 因此对于此问题可以在后端通过接口拿到数据后,找到已流转的连线,将它移动到最后;此时需要使用XPath查询。

1、需要的jar

  • Dom4J

  • Jaxen

2、使用

  • 模拟接口
public static Node getGraphNode() throws Exception{
	SAXReader reader = new SAXReader();
	Document document = reader.read(XmlTest.class.getResourceAsStream("../../graph.xml"));
	return document;
}

如上所示,该接口返回一个Node。

  • 使用selectSingleNode查询
Node selectSingleNode(java.lang.String xpathExpression)
Node node = getGraphNode();
//<graph>
Element graph = node.getDocument().getRootElement();
//<root>
Element root = (Element) graph.selectSingleNode("root");

selectSingleNode中直接用nodename查询表示:选取此节点的所有子节点。但如果此节点有多个(例如从root中查找node节点),此方法返回第一个满足条件的节点。

对于使用XPath查找linkAC这条线有多种方式,举例两种:

  • 第一种:

      Node linkAC = graph.selectSingleNode("//link[@id=\"linkAC\"]");
    
    • //

      从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。

    • [@id="xxx"]

      过滤出id为xxx的节点

  • 第二种:

      Node linkAC = graph.selectSingleNode("/graph/root/link[@id=\"linkAC\"]");
    
    • /

      从根节点选取。

  • 移动节点

我没有找到能移动节点的方法,因此先将找到的节点remove,然后再使用add添加。

root.remove(linkAC);
root.add(linkAC);

注意:此处调用移除和添加方法的是查找到节点的父节点

再调用root.asXML(),可以看到linkAC已被移动到了最后:

<link id="linkAB" source="nodeA" target="nodeB"/>
<link id="linkAC" source="nodeA" target="nodeC"/>

在前端渲染后也就得到了想要的效果: