3

Is it possible to translate keys to class properties in dataclasses? The reason is that the keys in the dict (which I cut out from a JSON file) does not have matching attribute names in JSON (e.g. _id in the below example has to become id).

With the code below I feel like I'm writing too much boilerplate.

Dict:

{'_id': '5719fdf6edf5136d37aaf562', 'divespot': 'The Tugs', 'divesite': 'Fathom Five National Park', 'region': 'Ontario', 'country': 'Canada'}

class:

class DiveSpot:
    id: str
    name: str

    def from_dict(self, divespot):
        self.id = divespot.get("_id")
        self.name = divespot.get("divespot")
bluppfisk
  • 2,538
  • 3
  • 27
  • 56
  • The idea of a class is that its attributes have meaning beyond just being generic data - the idea of a dictionary is that it can hold generic (if structured) data. If you're asking if it's possible to generate data class definitions based on dictionaries, the answer is likely "maybe" or "it's complicated", but why would you want to do that? What benefit do you expect to get from the data classes that you can't get by just leaving the data in dictionaries? – Grismar Aug 07 '22 at 00:18
  • I can collect all the classes and call something like to_uddf() on them to output them in another format. You're probably right that I'm approaching the problem in the wrong way, but that's why I'm here. Do you have a suggestion? – bluppfisk Aug 07 '22 at 00:30
  • 2
    Does [Initialize Python dataclass from dictionary](https://stackoverflow.com/questions/68417319/initialize-python-dataclass-from-dictionary) help? – Steven Rumbalski Aug 07 '22 at 00:37
  • it would; but to be fair the whole reason I chose dataclasses was to avoid the boilerplate associated with constructing objects. – bluppfisk Aug 07 '22 at 00:56
  • I should clarify that dataclasses_json lets you do this. – bluppfisk Aug 07 '22 at 00:57
  • 2
    You say you want to create data classes to be able to write them to other formats more easily, but a dictionary would allow you the same capability - it's still entirely unclear why you'd want to go the route of data classes. What you're asking can be done, but there appears to be no good reason to do so. – Grismar Aug 07 '22 at 01:32
  • fair enough. It just seemed like a clean and declarative way to do this without extra boilerplate but I can do without. – bluppfisk Aug 07 '22 at 06:30
  • 1
    unfortunately I can't post an answer as the q is closed, however check out [this gist](https://gist.github.com/rnag/ddd678b42af0ed97193c8b763d216d58) I added for a pretty straightforward walkthrough that I feel is pretty pythonic and easy to understand. – rv.kvetch Aug 08 '22 at 19:09
  • I much appreciate that answer and the implementation that feels indeed pythonic and follows the dataclasses_json implementation. Even though I've sucked it up and went another way with my code, I still think this would be worthy of an answer. I've voted to reopen. – bluppfisk Aug 09 '22 at 10:24

2 Answers2

0
from dataclasses import dataclass

dskeymap = {'_id': 'id', 'divespot': 'name', 'divesite': 'site', 'region': 'region', 'country': 'country'}
data =  {'_id': '5719fdf6edf5136d37aaf562', 'divespot': 'The Tugs', 'divesite': 'Fathom Five National Park', 
    'region': 'Ontario', 'country': 'Canada'}

@dataclass
class DiveSpot:
    id: str
    name: str
    site: str
    region: str
    country: str

ds = DiveSpot(**{newk: data[oldk] for oldk, newk in dskeymap.items()})

Printing ds gives

DiveSpot(id='5719fdf6edf5136d37aaf562', name='The Tugs', site='Fathom Five National Park', region='Ontario', country='Canada')

Or add a @classmethod

@dataclass
class DiveSpot:
    id: str
    name: str
    site: str
    region: str
    country: str

    @classmethod
    def fromdict(cls, d, keymap=dskeymap):
        return cls(**{newk: data[oldk] for oldk, newk in dskeymap.items()})
Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
-1

You can first transform the dictionary before creating the dataclass, for example:

divespot_dict = dict(divespot)
divespot_copy['id'] = divespot_dict['_id']
divespot = DiveSpot(**divespot_copy)
Karol
  • 1,246
  • 2
  • 13
  • 20