Repository: networkx/networkx

Issue:
Deserializing custom default properties from yEd graphML files
When importing a yEd created graphml file with custom properties on the 
nodes I found that the default values for boolean properties was always True.

After some browsing through the code of GraphMLReader I found that while
the properties of the nodes themselves are passed through the `decode_data_elements`
method (so that GraphML booleans are parsed correctly), the items in the 
node_default dictionary are not. 

For the defaults, the value of each key is just cast to the appropriate `python_type`
In the case of booleans with value='false' this results in `bool('false') == True`  :(


### Expected Behavior
The default attribute values should also be passed through `decode_data_elements`

### Steps to Reproduce
Make a yed graphml file with some custom node property of value 'boolean' with default 'false'
import the file using GraphMLReader
The resulting default node property has bool value True.

### Environment
Python version:
Python 3.9.4 (default, Apr  6 2021, 11:23:37) 
[Clang 11.0.3 (clang-1103.0.32.62)] on darwin

NetworkX version:
2.5.1

### Additional context
As posted in google discussion group
[https://groups.google.com/g/networkx-discuss/c/XbFTyx-PKqc/m/pD2_WAdYAQAJ](https://groups.google.com/g/networkx-discuss/c/XbFTyx-PKqc/m/pD2_WAdYAQAJ)



Discussion:
We should add code to send the default values through the `decode_data_elements` method -- similar to what we do with the non-default properties of the nodes.

Resolving patch:
diff --git a/networkx/readwrite/graphml.py b/networkx/readwrite/graphml.py
--- a/networkx/readwrite/graphml.py
+++ b/networkx/readwrite/graphml.py
@@ -962,8 +962,15 @@ def find_graphml_keys(self, graph_element):
                 "type": self.python_type[attr_type],
                 "for": k.get("for"),
             }
-            # check for "default" subelement of key element
+            # check for "default" sub-element of key element
             default = k.find(f"{{{self.NS_GRAPHML}}}default")
             if default is not None:
-                graphml_key_defaults[attr_id] = default.text
+                # Handle default values identically to data element values
+                python_type = graphml_keys[attr_id]["type"]
+                if python_type == bool:
+                    graphml_key_defaults[attr_id] = self.convert_bool[
+                        default.text.lower()
+                    ]
+                else:
+                    graphml_key_defaults[attr_id] = python_type(default.text)
         return graphml_keys, graphml_key_defaults


Test patch:
diff --git a/networkx/readwrite/tests/test_graphml.py b/networkx/readwrite/tests/test_graphml.py
--- a/networkx/readwrite/tests/test_graphml.py
+++ b/networkx/readwrite/tests/test_graphml.py
@@ -119,6 +119,40 @@ def setup_class(cls):
         cls.attribute_graph.add_edge("n5", "n4", id="e6", weight=1.1)
         cls.attribute_fh = io.BytesIO(cls.attribute_data.encode("UTF-8"))
 
+        cls.node_attribute_default_data = """<?xml version="1.0" encoding="UTF-8"?>
+        <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
+                http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+          <key id="d0" for="node" attr.name="boolean_attribute" attr.type="boolean"><default>false</default></key>
+          <key id="d1" for="node" attr.name="int_attribute" attr.type="int"><default>0</default></key>
+          <key id="d2" for="node" attr.name="long_attribute" attr.type="long"><default>0</default></key>
+          <key id="d3" for="node" attr.name="float_attribute" attr.type="float"><default>0.0</default></key>
+          <key id="d4" for="node" attr.name="double_attribute" attr.type="double"><default>0.0</default></key>
+          <key id="d5" for="node" attr.name="string_attribute" attr.type="string"><default>Foo</default></key>
+          <graph id="G" edgedefault="directed">
+            <node id="n0"/>
+            <node id="n1"/>
+            <edge id="e0" source="n0" target="n1"/>
+          </graph>
+        </graphml>
+        """
+        cls.node_attribute_default_graph = nx.DiGraph(id="G")
+        cls.node_attribute_default_graph.graph["node_default"] = {
+            "boolean_attribute": False,
+            "int_attribute": 0,
+            "long_attribute": 0,
+            "float_attribute": 0.0,
+            "double_attribute": 0.0,
+            "string_attribute": "Foo",
+        }
+        cls.node_attribute_default_graph.add_node("n0")
+        cls.node_attribute_default_graph.add_node("n1")
+        cls.node_attribute_default_graph.add_edge("n0", "n1", id="e0")
+        cls.node_attribute_default_fh = io.BytesIO(
+            cls.node_attribute_default_data.encode("UTF-8")
+        )
+
         cls.attribute_named_key_ids_data = """<?xml version='1.0' encoding='utf-8'?>
 <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -351,6 +385,11 @@ def test_read_attribute_graphml(self):
         for a, b in zip(ge, he):
             assert a == b
 
+    def test_node_default_attribute_graphml(self):
+        G = self.node_attribute_default_graph
+        H = nx.read_graphml(self.node_attribute_default_fh)
+        assert G.graph["node_default"] == H.graph["node_default"]
+
     def test_directed_edge_in_undirected(self):
         s = """<?xml version="1.0" encoding="UTF-8"?>
 <graphml xmlns="http://graphml.graphdrawing.org/xmlns"