Parsing Suds SOAP complex data type into Python di

2019-03-20 15:35发布

I have some data coming from a SOAP API using Suds which I need to parse in my Python script. Before I go off and write a parser (there is more than just this one to do):

1) Does anyone recognise what this is? It's the standard complex object datatype as returned by Suds (documentation). Should have spotted that.

2) If so, is there an existing library that I can use to convert it to a Python dictionary? How do I parse this object into a Python dict? It seems I can pass a dictionary to Suds but can't see an easy way of getting one back out.

(ArrayOfBalance){
   Balance[] = 
      (Balance){
         Amount = 0.0
         Currency = "EUR"
      },
      (Balance){
         Amount = 0.0
         Currency = "USD"
      },
      (Balance){
         Amount = 0.0
         Currency = "GBP"
      },
 }

6条回答
干净又极端
2楼-- · 2019-03-20 16:01

The right answer, as is often the case, is in the docs. The bits in (brackets) are objects which can contain other objects or types.

In this case we have an ArrayOfBalance object which contains a list of Balance types, each of which has the attributes of Amount and Currency.

These can all be referred to using . notation so the following one-liner does the trick.

balance = {item.Currency: item.Amount for item in response.Balance}  
查看更多
Luminary・发光体
3楼-- · 2019-03-20 16:02

There is a class method called dict in suds.client.Client class which takes a sudsobject as input and returns a Python dict as output. Check it out here: Official Suds Documentation

The resulting snippet becomes as elegant as this:

from suds.client import Client

# Code to obtain your suds_object here...

required_dict = Client.dict(suds_object)

You might also want to check out items class method (link) in the same class which extracts items from suds_object similar to items method on dict.

查看更多
干净又极端
4楼-- · 2019-03-20 16:10

Found one solution:

from suds.sudsobject import asdict

def recursive_asdict(d):
    """Convert Suds object into serializable format."""
    out = {}
    for k, v in asdict(d).iteritems():
        if hasattr(v, '__keylist__'):
            out[k] = recursive_asdict(v)
        elif isinstance(v, list):
            out[k] = []
            for item in v:
                if hasattr(item, '__keylist__'):
                    out[k].append(recursive_asdict(item))
                else:
                    out[k].append(item)
        else:
            out[k] = v
    return out

def suds_to_json(data):
    return json.dumps(recursive_asdict(data))

If subs are just nested dict and list, it should work.

查看更多
爷、活的狠高调
5楼-- · 2019-03-20 16:16

I was encountering a similar problem and had to read a suds response. Suds response will be returned as a tuple consisting of objects. len(response) will show you the number of objects contained in the suds response tuple. In order to access the first object we need to give response[0].

In IDLE if you type >>>print response[0] followed by a period '.' symbol you will get a popup showing the various objects that can be accessed from this level.

Example: if you type response[0]. it will bring a popup and show the Balance object so the command would now become response[0].Balance.

You can follow the same approach to get the list of objects under the subsequent levels

查看更多
Luminary・发光体
6楼-- · 2019-03-20 16:18

The checkaayush's answer is not recursive so, it does not consider the nested objects.

Based on aGuegu Answer i did some changes to solve an issue when the suds object has dicts inside lists.

It works!


from suds.sudsobject import asdict

def recursive_asdict(d):
    """Convert Suds object into serializable format."""
    out = {}
    for k, v in asdict(d).items():
        if hasattr(v, '__keylist__'):
            out[k] = recursive_asdict(v)
        elif isinstance(v, list):
            out[k] = []
            for item in v:
                if hasattr(item, '__keylist__'):
                    out[k].append(recursive_asdict(item))
                elif not isinstance(item, list):
                    out[k] = item
                else:
                    out[k].append(item)
        else:
            out[k] = v
    return out
查看更多
太酷不给撩
7楼-- · 2019-03-20 16:26

You can cast the object to dict(), but you still get the complex data type used by suds. So here are some helpful functions that I wrote just for the occasion:

def basic_sobject_to_dict(obj):
    """Converts suds object to dict very quickly.
    Does not serialize date time or normalize key case.
    :param obj: suds object
    :return: dict object
    """
    if not hasattr(obj, '__keylist__'):
        return obj
    data = {}
    fields = obj.__keylist__
    for field in fields:
        val = getattr(obj, field)
        if isinstance(val, list):
            data[field] = []
            for item in val:
                data[field].append(basic_sobject_to_dict(item))
        else:
            data[field] = basic_sobject_to_dict(val)
    return data


def sobject_to_dict(obj, key_to_lower=False, json_serialize=False):
    """
    Converts a suds object to a dict.
    :param json_serialize: If set, changes date and time types to iso string.
    :param key_to_lower: If set, changes index key name to lower case.
    :param obj: suds object
    :return: dict object
    """
    import datetime

    if not hasattr(obj, '__keylist__'):
        if json_serialize and isinstance(obj, (datetime.datetime, datetime.time, datetime.date)):
            return obj.isoformat()
        else:
            return obj
    data = {}
    fields = obj.__keylist__
    for field in fields:
        val = getattr(obj, field)
        if key_to_lower:
            field = field.lower()
        if isinstance(val, list):
            data[field] = []
            for item in val:
                data[field].append(sobject_to_dict(item, json_serialize=json_serialize))
        else:
            data[field] = sobject_to_dict(val, json_serialize=json_serialize)
    return data


def sobject_to_json(obj, key_to_lower=False):
    """
    Converts a suds object to json.
    :param obj: suds object
    :param key_to_lower: If set, changes index key name to lower case.
    :return: json object
    """
    import json
    data = sobject_to_dict(obj, key_to_lower=key_to_lower, json_serialize=True)
    return json.dumps(data)

If there is an easier way, I would love to hear about it.

查看更多
登录 后发表回答