Skip to content

Commit

Permalink
Refactor JSON serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick-Hall committed Oct 27, 2024
1 parent ddf7d2e commit 1dd0a91
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 50 deletions.
43 changes: 43 additions & 0 deletions gramps/gen/lib/baseobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -57,6 +58,48 @@ def unserialize(self, data):
Convert a serialized tuple of data to an object.
"""

def get_object_state(self):
"""
Get the current object state as a dictionary.
By default this returns the public attributes of the instance. This
method can be overridden if the class requires other attributes or
properties to be saved.
This method is called to provide the information required to serialize
the object. If None is returned then the object will be represented as
null in JSON.
:returns: Returns a dictionary of attributes that represent the state
of the object or None.
:rtype: dict
"""
attr_dict = dict(
(key, value)
for key, value in self.__dict__.items()
if not key.startswith("_")
)
attr_dict["_class"] = self.__class__.__name__
return attr_dict

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
By default this sets the state of the object assuming that all items in
the dictionary map to public attributes. This method can be overridden
to set the state using custom functionality. For performance reasons
it is useful to set a property without calling its setter function. As
JSON provides no distinction between tuples and lists, this method can
also be use to convert lists into tuples where required.
:param attr_dict: A dictionary of attributes that represent the state of
the object.
:type attr_dict: dict
"""
self.__dict__.update(attr_dict)

def matches_string(self, pattern, case_sensitive=False):
"""
Return True if any text data in the object or any of it's child
Expand Down
24 changes: 23 additions & 1 deletion gramps/gen/lib/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Copyright (C) 2009-2013 Douglas S. Blank
# Copyright (C) 2013 Paul Franklin
# Copyright (C) 2013-2014 Vassilii Khachaturov
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -750,6 +750,28 @@ def unserialize(self, data):
raise DateError("Invalid date to unserialize")
return self

def get_object_state(self):
"""
Get the current object state as a dictionary.
We override this method to represent an empty date as null in JSON.
"""
if self.is_empty() and not self.text:
return None
else:
return super().get_object_state()

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to convert `dateval` into a tuple.
"""
if "dateval" in attr_dict:
attr_dict["dateval"] = tuple(attr_dict["dateval"])
super().set_object_state(attr_dict)

@classmethod
def get_schema(cls):
"""
Expand Down
24 changes: 23 additions & 1 deletion gramps/gen/lib/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2010 Michiel D. Nauta
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -136,6 +136,28 @@ def serialize(self, no_text_date=False):
self.private,
)

def get_object_state(self):
"""
Get the current object state as a dictionary.
We override this method to handle the `type` and `description` properties.
"""
attr_dict = super().get_object_state()
attr_dict["type"] = self.__type
attr_dict["description"] = self.__description
return attr_dict

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to handle the `type` and `description` properties.
"""
self.__type = attr_dict.pop("type")
self.__description = attr_dict.pop("description")
super().set_object_state(attr_dict)

@classmethod
def get_schema(cls):
"""
Expand Down
22 changes: 21 additions & 1 deletion gramps/gen/lib/eventref.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Copyright (C) 2010 Michiel D. Nauta
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2013 Doug Blank <[email protected]>
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -86,6 +86,26 @@ def serialize(self):
self.__role.serialize(),
)

def get_object_state(self):
"""
Get the current object state as a dictionary.
We override this method to handle the `role` property.
"""
attr_dict = super().get_object_state()
attr_dict["role"] = self.__role
return attr_dict

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to handle the `role` property.
"""
self.__role = attr_dict.pop("role")
super().set_object_state(attr_dict)

@classmethod
def get_schema(cls):
"""
Expand Down
29 changes: 27 additions & 2 deletions gramps/gen/lib/grampstype.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -136,6 +136,30 @@ def __init__(self, value=None):
if value is not None:
self.set(value)

def get_object_state(self):
"""
Get the current object state as a dictionary.
We override this method to handle the `value` and `string` properties.
"""
attr_dict = {"_class": self.__class__.__name__}
attr_dict["value"] = self.__value
attr_dict["string"] = self.__string
return attr_dict

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to handle the `value` and `string` properties.
"""
self.__value = attr_dict["value"]
if self.__value == self._CUSTOM:
self.__string = attr_dict["string"]
else:
self.__string = ""

def __set_tuple(self, value):
"Set the value/string properties from a tuple."
val, strg = self._DEFAULT, ""
Expand Down Expand Up @@ -225,7 +249,8 @@ def get_schema(cls):
"title": _("Type"),
"properties": {
"_class": {"enum": [cls.__name__]},
"string": {"type": "string", "title": _("Type")},
"string": {"type": "string", "title": _("Custom type")},
"value": {"type": "integer", "title": _("Type code")},
},
}

Expand Down
14 changes: 13 additions & 1 deletion gramps/gen/lib/mediaref.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Copyright (C) 2010 Michiel D. Nauta
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2013 Doug Blank <[email protected]>
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -151,6 +151,18 @@ def unserialize(self, data):
RefBase.unserialize(self, ref)
return self

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to convert `rect` into a tuple.
"""
rect = attr_dict["rect"]
if rect is not None:
attr_dict["rect"] = tuple(rect)
super().set_object_state(attr_dict)

def get_text_data_child_list(self):
"""
Return the list of child objects that may carry textual data.
Expand Down
28 changes: 24 additions & 4 deletions gramps/gen/lib/person.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2010 Michiel D. Nauta
# Copyright (C) 2010,2017 Nick Hall
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2010 Michiel D. Nauta
# Copyright (C) 2010,2017,2024 Nick Hall
# Copyright (C) 2011 Tim G L Lyons
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -322,6 +322,26 @@ def unserialize(self, data):
TagBase.unserialize(self, tag_list)
return self

def get_object_state(self):
"""
Get the current object state as a dictionary.
We override this method to handle the `gender` property.
"""
attr_dict = super().get_object_state()
attr_dict["gender"] = self.__gender
return attr_dict

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to handle the `gender` property.
"""
self.__gender = attr_dict.pop("gender")
super().set_object_state(attr_dict)

def _has_handle_reference(self, classname, handle):
"""
Return True if the object has reference to a given handle of given
Expand Down
38 changes: 9 additions & 29 deletions gramps/gen/lib/serialize.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -37,38 +37,18 @@
import gramps.gen.lib as lib


def __default(obj):
obj_dict = {"_class": obj.__class__.__name__}
if isinstance(obj, lib.GrampsType):
obj_dict["string"] = getattr(obj, "string")
if isinstance(obj, lib.Date):
if obj.is_empty() and not obj.text:
return None
for key, value in obj.__dict__.items():
if not key.startswith("_"):
obj_dict[key] = value
for key, value in obj.__class__.__dict__.items():
if isinstance(value, property):
if key != "year":
obj_dict[key] = getattr(obj, key)
return obj_dict


def __object_hook(obj_dict):
obj = getattr(lib, obj_dict["_class"])()
for key, value in obj_dict.items():
if key != "_class":
if key in ("dateval", "rect") and value is not None:
value = tuple(value)
if key == "ranges":
value = [tuple(item) for item in value]
setattr(obj, key, value)
if obj_dict["_class"] == "Date":
if obj.is_empty() and not obj.text:
return None
_class = obj_dict.pop("_class")
cls = lib.__dict__[_class]
obj = cls.__new__(cls)
obj.set_object_state(obj_dict)
return obj


def __default(obj):
return obj.get_object_state()


def to_json(obj):
"""
Encode a Gramps object in JSON format.
Expand Down
23 changes: 22 additions & 1 deletion gramps/gen/lib/styledtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# Copyright (C) 2008 Zsolt Foldvari
# Copyright (C) 2013 Doug Blank <[email protected]>
# Copyright (C) 2017 Nick Hall
# Copyright (C) 2017,2024 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -102,6 +102,27 @@ def __init__(self, text="", tags=None):
else:
self._tags = []

def get_object_state(self):
"""
Get the current object state as a dictionary.
We override this method to handle the `tags` and `string` properties.
"""
attr_dict = {"_class": self.__class__.__name__}
attr_dict["tags"] = self._tags
attr_dict["string"] = self._string
return attr_dict

def set_object_state(self, attr_dict):
"""
Set the current object state using information provided in the given
dictionary.
We override this method to handle the `tags` and `string` properties.
"""
self._tags = attr_dict["tags"]
self._string = attr_dict["string"]

# special methods

def __str__(self):
Expand Down
Loading

0 comments on commit 1dd0a91

Please sign in to comment.