I have a data structure, I want to turn it into a json. Here my classes:
@dataclass
class RType:
type_name: str
class Meta:
name = '@r_type'
content = 'type_name'
@dataclass
class Options:
type: RType
content: str
class Meta:
name = 'options'
content = 'content'
What I want to get in the result (we always have r_type
in nested and normal structures):
{
'@r_type' : 'init',
'options': {
'@r_type' : 'options',
'options' : 'some_options'
}
another example
{
'@r_type' : 'dir_init',
'directory': {
'@r_type' : 'directory',
'directory' : 'path_to'
}
The problem is that we can easily convert a regular class to json using addict
or libraries, but my variable name contains '@'
How can I do this? (ideally without using libraries)
CodePudding user response:
This does what you ask in a mostly sane manner. The issue I have with your structure is that RType
is not an object type. It's just an attribute of your other types.
This modification of my original script adds a function to move the r_types
value to the front of each dict. This is not a completely general solution; if you have lists nested in lists, it will need changing. As it is, it works with dicts, or with lists containing dicts.
But your JSON reader is defective. You should file a bug report.
from dataclasses import dataclass, asdict
import json
@dataclass
class Options:
options: str
r_type: str = "options"
@dataclass
class Init:
options: Options
r_type: str = "init"
def movetypes(dct):
newdct = {}
if 'r_type' in dct:
newdct['@r_type'] = dct['r_type']
for key,value in dct.items():
if key == 'r_type':
continue
if isinstance(value,dict):
value = movetypes(value)
elif isinstance(value,list):
value = [movetypes(v) for v in value]
newdct[key] = value
return newdct
x = Options('some_options')
y = Init([x])
dct = asdict(y)
print(dct)
dct = movetypes(dct)
print(dct)
js = json.dumps(dct)
print(js)
Output:
{'options': [{'options': 'some_options', 'r_type': 'options'}], 'r_type': 'init'}
{'@r_type': 'init', 'options': [{'@r_type': 'options', 'options': 'some_options'}]}
{"@r_type": "init", "options": [{"@r_type": "options", "options": "some_options"}]}
CodePudding user response:
I know you asked for a solution without libraries, but here's a clean way which actually looks Pythonic to me at least. This does make use of an external library, dataclass-wizard
. The json_field
is synonymous usage to dataclasses.field
, but specifies an alias used for (de)serialization.
Note also: I've needed to swap the order of the fields, so that requires declaring options
field as an optional field. Since dictionary order should be the order in which the dataclass fields are defined, this ensures @r_type
shows up first in the serialized dict result.
import json
from dataclasses import dataclass
from dataclass_wizard import asdict, json_field
@dataclass
class Init:
r_type: str = json_field('@r_type', all=True, default='init')
options: 'Options' = None
@dataclass
class Options:
r_type: str = json_field('@r_type', all=True, default='options')
options: str = None
x = Options(options='some_options')
y = Init(options=[x])
dct = asdict(y)
# print(dct)
# {'@r_type': 'init', 'options': [{'@r_type': 'options', 'options': 'some_options'}]}
print(json.dumps(dct, indent=2))
Output:
{
"@r_type": "init",
"options": [
{
"@r_type": "options",
"options": "some_options"
}
]
}