Transform a value in preparation for serializing as json

    no-op for strings, mappings and iterables have their entries made safe,
    and all other values are stringified, with a fallback value if that fails

def safe_for_serialization(value):
    """ Transform a value in preparation for serializing as json

    no-op for strings, mappings and iterables have their entries made safe,
    and all other values are stringified, with a fallback value if that fails
    """

    if isinstance(value, six.string_types):
        return value
    if isinstance(value, dict):
        return {
            safe_for_serialization(key): safe_for_serialization(val)
            for key, val in six.iteritems(value)
        }
    if isinstance(value, collections.Iterable):
        return list(map(safe_for_serialization, value))

    try:
        return six.text_type(value)
    except Exception:
        return '[__unicode__ failed]'