def serializable(obj: Any, on_filter: Callable[[Any, str], bool] = None) -> Any:
    """
    Ensures the specified object is serializable, converting if necessary.

    :param obj: the object to use.
    :param on_filter: optional function that can be used to filter which
                      properties on the object will be included.
    :return value representing the object, which is serializable.
    """

    # Will be called recursively when object has children
    def _serializable(parent_obj: Any, obj: Any,
                      on_filter: Callable[[Any, str], bool]) -> Any:
        # None can be left as-is
        if obj is None:
            return obj

        # IntFlag enums should be broken down to a list of names
        elif isinstance(obj, IntFlag):
            value = str(obj)
            if value == '0':
                return None
            return value.split('|')

        # Any other enum just use the name
        elif isinstance(obj, Enum):
            return str(obj)

        # Simple types can be left as-is
        elif isinstance(obj, (bool, int, float, str)):
            return obj

        # Class supports method to convert to serializable dictionary; use it
        elif hasattr(obj, 'as_dict') and parent_obj is not None:
            return obj.as_dict()

        elif isinstance(obj, dict):
            # Dictionaries will require us to check each key and value
            new_dict = {}
            for item in obj.items():
                new_dict[_serializable(obj, item[0], on_filter=on_filter)] = \
                    _serializable(obj, item[1], on_filter=on_filter)
            return new_dict

        elif isinstance(obj, (list, Container)):
            # Lists will require us to check each item
            items = obj # type: Iterable
            new_list = []
            for item in items:
                new_list.append(_serializable(obj, item, on_filter=on_filter))
            return new_list

        # Convert to a dictionary of property name/values
        data = {}
        for name in dir(obj.__class__):
            if not isinstance(getattr(obj.__class__, name), property):
                continue
            elif on_filter and not on_filter(obj, name):
                continue
            value = getattr(obj, name)
            data[name] = _serializable(obj, value, on_filter=on_filter)
        return data

    return _serializable(None, obj, on_filter)
def serializable ( obj : Any , on_filter : Callable [ [ Any , str ] , bool ] = None ) -> Any : def _serializable ( parent_obj : Any , obj : Any , on_filter : Callable [ [ Any , str ] , bool ] ) -> Any : if obj is None : return obj elif isinstance ( obj , IntFlag ) : value = str ( obj ) if value == '0' : return None return value . split ( '|' ) elif isinstance ( obj , Enum ) : return str ( obj ) elif isinstance ( obj , ( bool , int , float , str ) ) : return obj elif hasattr ( obj , 'as_dict' ) and parent_obj is not None : return obj . as_dict ( ) elif isinstance ( obj , dict ) : new_dict = { } for item in obj . items ( ) : new_dict [ _serializable ( obj , item [ 0 ] , on_filter = on_filter ) ] = _serializable ( obj , item [ 1 ] , on_filter = on_filter ) return new_dict elif isinstance ( obj , ( list , Container ) ) : items = obj new_list = [ ] for item in items : new_list . append ( _serializable ( obj , item , on_filter = on_filter ) ) return new_list data = { } for name in dir ( obj . __class__ ) : if not isinstance ( getattr ( obj . __class__ , name ) , property ) : continue elif on_filter and not on_filter ( obj , name ) : continue value = getattr ( obj , name ) data [ name ] = _serializable ( obj , value , on_filter = on_filter ) return data return _serializable ( None , obj , on_filter )
Ensures the specified object is serializable, converting if necessary.

    :param obj: the object to use.
    :param on_filter: optional function that can be used to filter which
                      properties on the object will be included.
    :return value representing the object, which is serializable.