Returns a JSON encoder that can handle:
        * :obj:`numpy.ndarray`
        * :obj:`numpy.floating` (converted to :obj:`float`)
        * :obj:`numpy.integer` (converted to :obj:`int`)
        * :obj:`numpy.dtype`
        * :obj:`astropy.units.Quantity`
        * :obj:`astropy.coordinates.SkyCoord`

    Args:
        ndarray_mode (Optional[:obj:`str`]): Which method to use to serialize
            :obj:`numpy.ndarray` objects. Defaults to :obj:`'b64'`, which converts the
            array data to binary64 encoding (non-human-readable), and stores the
            datatype/shape in human-readable formats. Other options are
            :obj:`'readable'`, which produces fully human-readable output, and
            :obj:`'npy'`, which uses numpy's built-in :obj:`save` function and
            produces completely unreadable output. Of all the methods :obj:`'npy'`
            is the most reliable, but also least human-readable. :obj:`'readable'`
            produces the most human-readable output, but is the least reliable
            and loses precision.

    Returns:
        A subclass of :obj:`json.JSONEncoder`.

def get_encoder(ndarray_mode='b64'):
    """
    Returns a JSON encoder that can handle:
        * :obj:`numpy.ndarray`
        * :obj:`numpy.floating` (converted to :obj:`float`)
        * :obj:`numpy.integer` (converted to :obj:`int`)
        * :obj:`numpy.dtype`
        * :obj:`astropy.units.Quantity`
        * :obj:`astropy.coordinates.SkyCoord`

    Args:
        ndarray_mode (Optional[:obj:`str`]): Which method to use to serialize
            :obj:`numpy.ndarray` objects. Defaults to :obj:`'b64'`, which converts the
            array data to binary64 encoding (non-human-readable), and stores the
            datatype/shape in human-readable formats. Other options are
            :obj:`'readable'`, which produces fully human-readable output, and
            :obj:`'npy'`, which uses numpy's built-in :obj:`save` function and
            produces completely unreadable output. Of all the methods :obj:`'npy'`
            is the most reliable, but also least human-readable. :obj:`'readable'`
            produces the most human-readable output, but is the least reliable
            and loses precision.

    Returns:
        A subclass of :obj:`json.JSONEncoder`.
    """

    # Use specified numpy.ndarray serialization mode
    serialize_fns = {
        'b64': serialize_ndarray_b64,
        'readable': serialize_ndarray_readable,
        'npy': serialize_ndarray_npy}

    if ndarray_mode not in serialize_fns:
        raise ValueError('"ndarray_mode" must be one of {}'.format(
            serialize_fns.keys))

    serialize_ndarray = serialize_fns[ndarray_mode]

    class MultiJSONEncoder(json.JSONEncoder):
        """
        A JSON encoder that can handle:
            * :obj:`numpy.ndarray`
            * :obj:`numpy.floating` (converted to :obj:`float`)
            * :obj:`numpy.integer` (converted to :obj:`int`)
            * :obj:`numpy.dtype`
            * :obj:`astropy.units.Quantity`
            * :obj:`astropy.coordinates.SkyCoord`
        """
        def default(self, o):
            if isinstance(o, coords.SkyCoord):
                return serialize_skycoord(o)
            if isinstance(o, units.Quantity):
                return serialize_quantity(o)
            elif isinstance(o, np.ndarray):
                return serialize_ndarray(o)
            elif isinstance(o, np.dtype):
                return serialize_dtype(o)
            elif isinstance(o, np.floating):
                return float(o)
            elif isinstance(o, np.integer):
                return int(o)
            elif isinstance(o, np.bool_):
                return bool(o)
            elif isinstance(o, np.void):
                try:
                    o = np.array(o)
                except:
                    pass
                else:
                    return o
            return json.JSONEncoder.default(self, o)

    return MultiJSONEncoder