Skip to content

Python driver: Vertex/Edge/Path model classes should provide dict/JSON serialization #2371

@uesleilima

Description

@uesleilima

Is your feature request related to a problem?

The driver's model classes (Vertex, Edge, Path in models.py) are returned by the ANTLR4 parser when deserializing agtype results. While these classes implement __getitem__/__setitem__ for property access, they do not implement:

  • to_dict() / as_dict() for conversion to plain Python dicts.
  • The Mapping protocol (__contains__, .get(), .keys(), .values(), .items()) for duck-typing compatibility.
  • __eq__ / __hash__ for comparison and use in sets/dicts.
  • JSON serialization support (json.dumps() raises TypeError).

This forces every consumer of the driver to write their own normalization layer to convert driver objects into standard Python data structures.

Current workaround

import age

def _to_dict(obj):
    """Convert AGE driver objects to plain dicts."""
    if obj is None:
        return None
    if isinstance(obj, age.models.Vertex):
        return {
            "id": obj.id,
            "label": obj.label,
            "properties": dict(obj.properties),
        }
    if isinstance(obj, age.models.Edge):
        return {
            "id": obj.id,
            "label": obj.label,
            "start_id": obj.start_id,
            "end_id": obj.end_id,
            "properties": dict(obj.properties),
        }
    if isinstance(obj, age.models.Path):
        return [_to_dict(element) for element in obj]
    return obj

Every query result must be passed through this function before use.

Describe the solution you'd like

Add to_dict() methods to Vertex, Edge, and Path:

class Vertex(AGObj):
    def to_dict(self):
        return {
            "id": self.id,
            "label": self.label,
            "properties": dict(self.properties) if self.properties else {},
        }

class Edge(AGObj):
    def to_dict(self):
        return {
            "id": self.id,
            "label": self.label,
            "start_id": self.start_id,
            "end_id": self.end_id,
            "properties": dict(self.properties) if self.properties else {},
        }

class Path(AGObj):
    def to_dict(self):
        return [e.to_dict() if isinstance(e, AGObj) else e for e in self.entities]

Optionally, also implement __eq__ and __hash__ based on id, and make the classes JSON-serializable (e.g., via a custom JSONEncoder or by implementing __json__).

Note: the classes already have toJson() methods that produce string representations, but these output custom formats (not standard JSON) and cannot be used with json.dumps().

Describe alternatives you've considered

  • Returning plain dicts from the parser instead of model classes — this would break backward compatibility.
  • Using dataclasses or Pydantic models — more Pythonic, but a larger refactor.

Environment

  • Python driver: master branch (psycopg3 version)
  • Python: 3.10+

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions