Parsing Suds SOAP complex data type into Python dict

前端 未结 6 1807
生来不讨喜
生来不讨喜 2020-12-31 23:24

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 (ther

相关标签:
6条回答
  • 2020-12-31 23:29

    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))
            elif isinstance(val, (datetime.datetime, datetime.time, datetime.date)):
                data[field] = val.isoformat()
            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.

    0 讨论(0)
  • 2020-12-31 23:30

    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
    
    0 讨论(0)
  • 2020-12-31 23:37

    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.

    0 讨论(0)
  • 2020-12-31 23:45

    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

    0 讨论(0)
  • 2020-12-31 23:48

    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.

    0 讨论(0)
  • 2020-12-31 23:50

    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}  
    
    0 讨论(0)
提交回复
热议问题