tags:

views:

203

answers:

4

I need to "flatten" objects into nested dicts of the object's properties. The objects I want to do this with are generally just containers for basic types or other objects which act in a similar way. For example:

class foo(object):
    bar = None
    baz = None

class spam(object):
    eggs = []

x = spam()
y = foo()
y.bar = True
y.baz = u"boz"
x.eggs.append(y)

What I need to "flatten" this to is:

{ 'eggs': [ { 'bar': True, 'baz': u'boz' } ] }

Is there anything in the stdlib which can do this for me? If not, would I have to test isinstance against all known base-types to ensure I don't try to convert an object which can't be converted (eg: bool)?

Edit:

These are objects are being returned to my code from an external library and therefore I have no control over them. I could use them as-is in my methods, but it would be easier (safer?) to convert them to dicts - especially for unit testing.

A: 

No, there is nothing in the standardlib. Yes, you would have to somehow test that the types are basic types like str, unicode, bool, int, float, long...

You could probably make a registry of methods to "serialize" different types, but that would only be useful if you have some types that should not have all it's attributes serialized, for example, or if you also need to flatten class attributes, etc.

Lennart Regebro
exactly what I wrote in my comment but you don't necessarily need to check for basic types, when your extended types support some kind of serialization method.Just try to call it on every type and catch the appropriate exception for base types.
Shirkrin
A: 

Almost every object has a dictionary (called __dict__), with all its methods and members.
With some type checking, you can then write a function that filters out only the members you are interested in.

It is not a big task, but as chrispy said, it could worth to try looking at your problem from a completely different perspective.

Roberto Liffredo
Thanks, but these are objects I'm being passed from another library so have no control over them. I could use them as-is in my methods, but it would be easier to convert them to dicts.
digitala
+1  A: 

Code: You may need to handle other iterable types though:

def flatten(obj):
    if obj is None:
        return None
    elif hasattr(obj, '__dict__') and obj.__dict__:
        return dict([(k, flatten(v)) for (k, v) in obj.__dict__.items()])
    elif isinstance(obj, (dict,)):
        return dict([(k, flatten(v)) for (k, v) in obj.items()])
    elif isinstance(obj, (list,)):
        return [flatten(x) for x in obj]
    elif isinstance(obj, (tuple,)):
        return tuple([flatten(x) for x in obj])
    else:
        return obj

Bug? In your code instead of:

class spam(object):
    eggs = []

x = spam()
x.eggs.add(...)

please do:

class spam(object):
    eggs = None #// if you need this line at all though

x = spam()
x.eggs = []
x.eggs.add(...)

If you do not, then all instances of spam will share the same eggs list.

van
A: 
mandel