Repository: networkx/networkx

Issue:
Reading GEXF1.3-Files
<!-- If you have a general question about NetworkX, please use the discussions tab to create a new discussion -->

<!--- Provide a general summary of the issue in the Title above -->

### Current Behavior

Currently it is not possible to read GEXF-Files with the version 1.3. As it is the code throws an NetworkXError that "No <graph> element in GEXF file". (gexf.py:707). 

### Expected Behavior

The graph should be read. 

###Analysis

As far I could see it is "just" a matter of add a new version string to the  class GEXF as there (at least in the non-dynamic version) are no really structural changes in the format specification. 

### Environment

Python version: 3.11.9
NetworkX version: 3.4.2



Discussion:
See also:  #7972 

@tschoellhorn is this still an issue in nx v3.5? That should improve support for "dynamic" graphs in gexf but that may not have been the original issue. If you have a minimal reproducing example to illustrate the bug that'd be helpful!



Patch:
diff --git a/networkx/readwrite/gexf.py b/networkx/readwrite/gexf.py
index da726e31989..c075c133cfa 100644
--- a/networkx/readwrite/gexf.py
+++ b/networkx/readwrite/gexf.py
@@ -204,6 +204,18 @@ class GEXF:
             ),
             "VERSION": "1.2",
         },
+        "1.3": {
+            "NS_GEXF": "http://gexf.net/1.3",
+            "NS_VIZ": "http://gexf.net/1.3/viz",
+            "NS_XSI": "http://w3.org/2001/XMLSchema-instance",
+            "SCHEMALOCATION": " ".join(
+                [
+                    "http://gexf.net/1.3",
+                    "http://gexf.net/1.3/gexf.xsd",
+                ]
+            ),
+            "VERSION": "1.3",
+        },
     }
 
     def construct_types(self):


Test patch:
diff --git a/networkx/readwrite/tests/test_gexf.py b/networkx/readwrite/tests/test_gexf.py
index d69a5ef5594..4e487cc6f4a 100644
--- a/networkx/readwrite/tests/test_gexf.py
+++ b/networkx/readwrite/tests/test_gexf.py
@@ -6,6 +6,39 @@
 import networkx as nx
 
 
+def test_gexf_v1_3(tmp_path):
+    """'Basic graph' example from https://gexf.net/schema.html"""
+    # GEXF file from published example
+    data = """<?xml version="1.0" encoding="UTF-8"?>
+<gexf xmlns="http://gexf.net/1.3" version="1.3">
+    <graph mode="static" defaultedgetype="directed">
+        <nodes>
+            <node id="0" label="Hello" />
+            <node id="1" label="Word" />
+        </nodes>
+        <edges>
+            <edge source="0" target="1" />
+        </edges>
+    </graph>
+</gexf>
+"""
+    with open(fname := (tmp_path / "basic.gexf"), "w") as fh:
+        fh.write(data)
+
+    # Expected output based on xml input
+    expected = nx.DiGraph([("0", "1")])
+    nx.set_node_attributes(expected, {"0": "Hello", "1": "Word"}, name="label")
+    expected.graph = {"mode": "static", "edge_default": {}}
+
+    # Load example with version explicitly set
+    G = nx.read_gexf(fname, version="1.3")
+    assert nx.utils.graphs_equal(G, expected)
+
+    # And with the "default" version
+    G = nx.read_gexf(fname)
+    assert nx.utils.graphs_equal(G, expected)
+
+
 @pytest.mark.parametrize("time_attr", ("start", "end"))
 @pytest.mark.parametrize("dyn_attr", ("static", "dynamic"))
 def test_dynamic_graph_has_timeformat(time_attr, dyn_attr, tmp_path):