I am getting Data via a REST-Interface and I want to store those data in a class-object.
my class could looks like this:
class Foo:
firstname = ''
lastname = ''
street = ''
number = ''
and the json may look like this:
[
{
"fname": "Carl",
"lname": "any name",
"address": ['carls street', 12]
}
]
What's the easiest way to map between the json and my class?
My problem is: I want to have a class with a different structure than the json.
I want the names of the attributes to be more meaningful.
Of course I know that I could simply write a to_json method and a from_json method which does what I want.
The thing is: I have a lot of those classes and I am looking for more declarative way to write the code.
e.g. in Java I probably would use mapstruct.
Thanks for your help!
Use a dict for the json input. Use **kwargs in an __init__ method in your class and map the variables accordingly.
I had a similar problem, and I solved it by using #classmethod
import json
class Robot():
def __init__(self, x, y):
self.type = "new-robot"
self.x = x
self.y = y
#classmethod
def create_robot(cls, sdict):
if sdict["type"] == "new-robot":
position = sdict["position"]
return cls(position['x'], position['y'])
else:
raise Exception ("Unable to create a new robot!!!")
if __name__=='__main__':
input_string = '{"type": "new-robot", "position": {"x": 3, "y": 3}}'
cmd = json.loads(input_string)
bot = Robot.create_robot(cmd)
print(bot.type)
Perhaps you could you two classes, one directly aligned with the Json (your source class) and the other having the actual structure you need. Then you could map them using the ObjectMapper class[https://pypi.org/project/object-mapper/]. This is very close to the MapStruct Library for Java.
ObjectMapper is a class for automatic object mapping. It helps you to create objects between project layers (data layer, service layer, view) in a simple, transparent way.
Related
food_data is a variable containing JSON data. Using the data, I want to create a list of Food objects, like so
foods = []
for data_row in food_data:
foods.append(Food(data_row))
This is what my Food class looks like as of right now:
class Food(dict):
""" by inheriting from dict, Food objects become automatically serializable for JSON formatting """
def __init__(self, data):
""" create a serialized food object with desired fields """
id = data["id"]
name = data["title"]
image = data["image"]
super().__init__(self, id=id, name=name, image=image)
And here is some example data:
[
{
"id": 738290,
"title": "Pasta with Garlic, Scallions, Cauliflower & Breadcrumbs",
"image": "https://spoonacular.com/recipeImages/716429-312x231.jpg",
},
{
"id": 343245,
"title": "What to make for dinner tonight?? Bruschetta Style Pork & Pasta",
"image": "https://spoonacular.com/recipeImages/715538-312x231.jpg",
}
]
Is there a method I can write for the Food class that will take the data and return a list of different versions of itself?
I would start by not subclassing dict: there is a better way to make an instance of Food serializable.
Next, make Food.__init__ dumb: three arguments, used to set three attributes.
Then, define a class method that is responsible for parsing an arbitrary dict with at least id, title, and image keys to get the values expected by Food.__init__.
Finally, define a method that turns an instance of Food back into a dict (though not necessarily the same dict that from_dict uses; generate one that serializes the way you want).
class Food:
def __init__(self, id, name, image):
self.id = id
self.name = name
self.image = image
#classmethod
def from_dict(cls, d):
return cls(id=d['id'], name=d['title'], image=d['image'])
def to_dict(self):
return dict(id=self.id, name=self.name, image=self.image)
foods = [Food.from_dict(d) for d in food_data]
To make your instance serializable, define a customer encoder that uses your to_dict method,
class FoodEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Food):
return obj.to_dict()
return super().default(obj)
This piggy backs on the default encoder; if the immediate object is a Food, default returns a serializable dict. Otherwise, it defers to its parent to try to serialize it.
Then use that class in the call to json.dumps.
print(json.dumps(foods, cls=FoodEncoder))
I think in my case I may have been over-engineering my code. But I received many responses that did help me out in other aspects so I'm going to offer them here:
#Juanpa
Use a list comprehension
foods = [Food[data] for data in food_data]
#Chepner - unrelated but useful
subclass json.JSONEncoder instead of dict for serializability
#Matthias
Create a staticmethod within the class to return a list of objects
#staticmethod
def create_foods(food_data):
foods = []
for data_row in food_data:
foods.append(Food(data_row))
The class I have been using looks simple, like this:
class Transaction(dict):
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
and then sending in:
transaction = Transaction({"to": "0x000", "from": "0x001": "timestamp": 1234})
and of course can be used like this transaction.to, however it looks like transaction.from does not work because from is a python reserved keyword
So I am curious using that simple class, is there a way to reassign from in the class to be something like
self.sender = dict.from
I have been trying with __init__ but with no luck
I also have written the class just with an __init__ and then assigning all values using self but with out a getter the class is not iterable
What I have been doing looks like this
# given data - {"to": "0x000", "from": "0x001": "timestamp": 1234}
item["sender"] = item["from"]
transaction = Transaction(item)
and then I have reference to it like transaction.sender.
If I understand correctly, your end goal is to have a class that can be instantiated from a dict and expose the keys as attributes. I'm inferring that the dict can only contain certain keys since you're talking about mapping "from" to sender. In that case, I would do this completely differently: don't subclass dict, instead have an alternate constructor that can handle the dict. I'd keep a "normal" constructor mostly for the sake of the repr.
For example:
class Transaction:
def __init__(self, to, from_, timestamp):
self.to = to
self.from_ = from_
self.timestamp = timestamp
#classmethod
def from_dict(cls, d):
return cls(d['to'], d['from'], d['timestamp'])
def __repr__(self):
"""Show construction."""
r = '{}({!r}, {!r}, {!r})'.format(
type(self).__name__,
self.to,
self.from_,
self.timestamp)
return r
transaction = Transaction.from_dict({"to": "0x000", "from": "0x001", "timestamp": 1234})
print(transaction) # -> Transaction('0x000', '0x001', 1234)
print(transaction.from_) # -> 0x001
Here I'm using the trailing underscore convention covered in PEP 8:
single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.
tkinter.Toplevel(master, class_='ClassName')
By the way, if it's useful, the keyword module contains the names of all Python keywords.
How can I use getattr without "Class" per se ?
So I have this situation: I have 'columns' that are asking mysql for specific data in a specific order. data is printed via flask/apache so that user has ability to manipulate this data. Now, From flask, POST methdd, I'm receiving changed(?) values and I am storing them in python attributes.I need to check if values within those attributes are same as in original data. Sure, I could hardcore it but I would like have possibility of change columns dynamically.
columns = ["username", "email", "admin"]
data = ("john", "john#snow.com", "True")
username = "john"
email = "different#email.com"
admin = False
Not sure how can I approach it ?
for i in data:
if i == getattr(???, 'username'):
print("it's the same")
or something like this?:
for i in data:
if i == getattr(data, '?????'):
print("it's the same")
Everything is within flask, I cannot embed it into the Class per se. So I don't have 'self' etc.
If I could create class I would probably make something like
class Myclass:
def __init__(self):
self.columns = ["username", "email", "admin"]
self.data = ("john", "john#snow.com", "True")
self.result = []
self.username = "john"
self.email = "different#email.com"
self.admin = False
def test(self):
for i in self.data:
if i == getattr(self, self.columns[self.data.index(i)]):
self.result.append("same")
else:
self.result.append("different")
return self.result
Myclass().test()
['same', 'different', 'different']
It turned out that I was looking for simple eval(). getattr() is designed for different purposes.
so simple:
for i in data:
if i == eval(cols[data.index(i)]):
print("it's the same")
did the trick
Flask is just Python code. You can create a class and use that if that fits your use-case. Or, if you used Flask-SQLAlchemy to manage database-backed data you'd have classes and instances anyway (and get easier data updates to boot).
And classes and instances are not the only objects with attributes; modules and functions have attributes too (although you wouldn't store your data as attributes on either of those), and when you look up methods on anything, you are looking up attributes too.
Pick a storage, then either wrap that storage with an instance of a class, and use getattr(), or pick a different data structure and use the methods for that data structure to get at the different fields. A dictionary, for instance, would make it trivial to get the current value for a given name.
If you do stick to instances, then note that in your loop you'd want to zip your columns and data values together:
for name, value in zip(columns, data):
if getattr(self, name) == value:
self.result.append("same")
else:
self.result.append("different")
Note that you do not have to add "self." in front, the whole point of getattr() is do the same work the . syntax does.
You probably want to put your columns and data lists together as a dictionary:
self.data = {'username': 'john', 'email': 'john#snow.com', 'admin': 'True'}
because that's how you'd process POST data from a form anyway; that way you can iterate over the dict.items() pairs, or use just the columns list to access values:
for name, value in self.data.items():
# ...
or use dict.get() to retrieve values, allowing for missing entries:
for name in self.columns:
if getattr(self, name) == self.data.get(name):
# ...
I need some advice. Two questions, does something already exist for this, what modules should I use to develop this.
I have some structures that come from an XML file. I want to represent them in Python Classes (maybe using a factory to create a class per structure). But I want these classes to have a function that will emit the structure as a C Struct.
From my research ctypes seems like the recommended thing to use to represent the structures in Python classes, but I don't see any methods for anything that will emit C Stucts for the creation of a header file.
From OP's comment I think the minimal solution a set of helper functions instead of classes. the xmltodict library makes it easy to turn the XML data into nested dictionaries, more or less like JSON. A set of helpers that parse the contents and generate appropriate C-struct strings is all that's really needed. If you can work with dictionaries :
{
"name": "my_struct",
"members": {
[
"name": "intmember",
"ctype": "int"
},
{
"name": "floatmember",
"ctype": "float"
}
]
}
You can do something like:
from string import Template
struct_template_string = '''
typedef $structname struct {
$defs
} $structname;
'''
struct_template = Template(struct_template_string)
member_template = Template(" $ctype $name;")
def spec_to_struct(spec_dict):
structname = spec_dict['name']
member_data = spec_dict['members']
members = [member_template.substitute(d) for d in member_data]
return struct_template.substitute(structname = structname, defs = "\n".join(members))
Which will produce something like:
typedef my_struct struct {
int intmember;
float floatmember;
} my_struct;
I'd try to get it working with basic functions first before trying to build up a class scaffold. It would be pretty easy to hide the details in a class using property descriptors:
class data_property(object):
def __init__(self, path, wrapper = None):
self.path = path
self.wrapper = wrapper
def __get__(self, instance, owner):
result = instance[self.path]
if self.wrapper:
if hasattr(result, '__iter__'):
return [self.wrapper(**i) for i in result]
return self.wrapper(**result)
return result
class MemberWrapper(dict):
name = data_property('name')
type = data_property('ctype')
class StructWrapper(dict):
name = data_property('name')
members = data_property('members', MemberWrapper )
test = StructWrapper(**example)
print test.name
print test.members
for member in test.members:
print member.type, member.name
# my_struct
# [{'name': 'intmember', 'ctype': 'int'}, {'name': 'floatmember', 'ctype': 'float'}]
# int intmember
# float floatmember
I'm working on a trivial problem here to deserialize some JSON (I cannot change the format, it's not a service I created) into Python objects. I've managed to do the conversion using lambda's, but I'd like to use an object_hook now, to see if the it's possible to do a conversion using the json.loads method. However, that's where I'm failing right now, and I was wondering if someone could point me in the right direction.
This is the code I currently have:
import json
class Light:
def __init__(self, id, name):
self.id = id
self.name = name
response = '{"1": {"name": "bedroom"}, "2": {"name": "kitchen"}}'
def object_decoder(obj):
return Light(......)
print json.loads(response, object_hook=object_decoder)
As you can see, the response is one document with two keys, named 1 and 2. It would be nice if I can make the the code work in a way that the json.loads would return two Light objects, but at the moment, I'm stuck, and I don't know how to iterate over response to make this work.
object_hook won't help you, since you have id and name on the different levels in the json string:
object_hook, if specified, will be called with the result of every
JSON object decoded and its return value will be used in place of the
given dict.
Let's see why object_hook won't help. If you print objects that are coming to the object_decoder function, you'll see that it is going up from the deep, like this:
{u'name': u'bedroom'}
{u'name': u'kitchen'}
{u'1': None, u'2': None}
None
This means that you cannot join object_decoder calls in order to produce a Light instance.
How about using custom JSONDecoder class instead:
import json
class Light:
def __init__(self, id, name):
self.id = id
self.name = name
response = '{"1": {"name": "bedroom"}, "2": {"name": "kitchen"}}'
class Decoder(json.JSONDecoder):
def decode(self, s):
obj = super(Decoder, self).decode(s)
return [Light(id=k, name=v['name']) for k, v in obj.iteritems()]
lights = json.loads(response, cls=Decoder)
print lights # prints [<__main__.Light instance at 0x9c3b50c>, <__main__.Light instance at 0x9c3b56c>]
print [light.__dict__ for light in lights] # prints [{'id': u'1', 'name': u'bedroom'}, {'id': u'2', 'name': u'kitchen'}]
This is actually the same as making json.loads() and then instantiate classes after.
If you are able to change the format of the string, I suggest you use jsonpickle. I've founded it perfect for this sort of thing.