Recursively marshal a Python object to a dict
        that can be passed to json.{dump,dumps}, a web client,
        or a web server, document database, etc...

    Args:
        obj:      object, It's members can be nested Python
                  objects which will be converted to dictionaries
        types:    tuple-of-types, The primitive types that can be
                  serialized
        method:   None-or-str, None to use 'marshal_dict' recursively,
                  or a str that corresponds to the name of a class method
                  to use.  Any nested types that are not an instance of
                  @types must have this method defined.
        fields:   None-list-of-str, Explicitly marshal only these fields
        m_kwargs: Keyword arguments to pass to @method
    Returns:
        dict

def marshal_dict(
    obj,
    types,
    method=None,
    fields=None,
    **m_kwargs
):
    """ Recursively marshal a Python object to a dict
        that can be passed to json.{dump,dumps}, a web client,
        or a web server, document database, etc...

    Args:
        obj:      object, It's members can be nested Python
                  objects which will be converted to dictionaries
        types:    tuple-of-types, The primitive types that can be
                  serialized
        method:   None-or-str, None to use 'marshal_dict' recursively,
                  or a str that corresponds to the name of a class method
                  to use.  Any nested types that are not an instance of
                  @types must have this method defined.
        fields:   None-list-of-str, Explicitly marshal only these fields
        m_kwargs: Keyword arguments to pass to @method
    Returns:
        dict
    """

    has_slots, d = _get_dict(obj)

    if fields:
        for field in fields:
            assert field in d
        return {
            k: v if isinstance(v, types) else (
                getattr(v, method)(**m_kwargs)
                if method
                else marshal_dict(v, types)
            )
            for k, v in d.items()
            if k in fields
        }

    excl = getattr(obj, '_marshal_exclude', [])

    if (
        has_slots
        or
        getattr(obj, '_marshal_only_init_args', False)
    ):
        args = init_args(obj)
        excl.extend([x for x in d if x not in args])

    if getattr(obj, '_marshal_exclude_none', False):
        excl.extend(k for k, v in d.items() if v is None)
    else:
        none_keys = getattr(obj, '_marshal_exclude_none_keys', [])
        if none_keys:
            excl.extend(x for x in none_keys if d.get(x) is None)

    return {
        k: v if isinstance(v, types) else (
            getattr(v, method)(**m_kwargs)
            if method
            else marshal_dict(v, types)
        )
        for k, v in d.items()
        if k not in excl
    }