Search code examples
javagraphgeojsonjgraphtgraphml

JgraphT GraphML importer failing on geojson properties


I am having an issue using the GraphMLImporter in JGraphT and I'm completely stumped.

As part of our serialization process, we export our graph to graphml format. That works fine; we can take the exported file and view it in gephi or any other graph parsing program without a problem. The issue happens when we try to import (deserialize) the file back in to a graph.

When we export, one of the fields on our object is a geometry. We need this, so we export it as a property of each vertex (each vertex is representative of a small region, the geometry is the associated region). When we read it back in, rather than giving me the full string as an attribute, for some reason we get only the last coordinate and some of the brackets. Which of course isn't valid geometry, and it chokes.

For reference, here is what we get from the exporter when we write our graph to graphml:

<?xml version="1.0" encoding="UTF-8"?><graphml xmlns="http://graphml.graphdrawing.org/xmlns" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<key id="key0" for="node" attr.name="lat" attr.type="double"/>
<key id="key1" for="node" attr.name="lng" attr.type="double"/>
<key id="key2" for="node" attr.name="geometry" attr.type="string"/>
<key id="key3" for="edge" attr.name="travel_time" attr.type="double"/>
<graph edgedefault="directed">
<node id="1">
<data key="key0">0.5</data>
<data key="key1">0.5</data>
<data key="key2">"{"type":"Polygon","coordinates":[[[0.0,0.0],[0.0,1.0],[1.0,1.0],[1.0,0.0],[0.0,0.0]]]}"</data>
</node>
<node id="2">
<data key="key0">0.5</data>
<data key="key1">1.5</data>
<data key="key2">"{"type":"Polygon","coordinates":[[[0.0,1.0],[0.0,2.0],[1.0,2.0],[1.0,1.0],[0.0,1.0]]]}"</data>
</node>
<edge id="1" source="1" target="2">
<data key="key3">1.0</data>
</edge>
<edge id="2" source="2" target="1">
<data key="key3">1.0</data>
</edge>
</graph>
</graphml>

When I deserialize the graph, the attributes come across correctly for key0 and key1 (those are lat longs) but the string for key2 (our geometry object) comes back as the following:

,[0.0,0.0]]]}"

As a result, I cannot deserialize the geometry.

So my question is, why the hell is this happening? Is this a limitation in the graphml format, maybe it can't contain geojson strings for some reason? Do I not know how to XML properly?

Any help would be appreciated.

Just so people know, though, I have been through the demo that Jgrapht provides for using the GraphML importer and exporter, but it doesn't really have any information on this. It only deals with very simple objects, not something this complicated.


Solution

  • I am posting this in case anyone else comes across the same issue. The problem appears to be a bug in the JGraphT GraphMLImporter. Specifically, this method:

        @Override
        public void characters(char ch[], int start, int length)
            throws SAXException
        {
            if (insideDefault) {
                currentKey.defaultValue = new String(ch, start, length);
            } else if (insideData) {
                currentData.value = new String(ch, start, length);
            }
        }
    

    The issue is that in order to determine currentData.value, the parser uses a combination of commas (,) and brackets (in my case, []) to determine when a value has been successfully "read". However, when the string element is a geojson object, the number of brackets and commas cause things to go haywire, and it breaks the value into chunks. It terminated on the final chunk, which is why I was getting part of the string but not the whole thing.

    The solution for us, which we will be submitting to JGraphT, is to instead append things to currentData.value, instead of creating a new one each time. Our solution makes the method look like this:

        @Override
        public void characters(char ch[], int start, int length)
            throws SAXException {
            if (insideDefault) {
                currentKey.defaultValue = new String(ch, start, length);
            } else if (insideData) {
                currentData.valueBuilder.append(ch, start, length);
            }
        }
    

    Hope that helps anybody else who has this problem