### Navigation

- index
- modules |
- vSphere Automation SDK for Python 6.5.0 documentation »
- Module code »

# Source code for vmware.vapi.lib.jsonlib

    """
    JSON encoder for double values
    """

    __author__ = 'VMware, Inc.'
    __copyright__ = 'Copyright (c) 2015 VMware, Inc.  All rights reserved.'

    import decimal
    import logging
    import math
    try:
        import simplejson as json
    except ImportError:
        import json

    from vmware.vapi.l10n.runtime import message_factory
    from vmware.vapi.exception import CoreException

    logger = logging.getLogger(__name__)

    def canonicalize_double(o):
        """
        Canonicalize double based on XML schema double canonical format

        The exponent must be indicated by "E". Leading zeroes and the
        preceding optional "+" sign are prohibited in the exponent. If the
        exponent is zero, it must be indicated by "E0". For the mantissa, the
        preceding optional "+" sign is prohibited and the decimal point is
        required. Leading and trailing zeroes are prohibited subject to the
        following: number representations must be normalized such that there
        is a single digit which is non-zero to the left of the decimal point
        and at least a single digit to the right of the decimal point unless
        the value being represented is zero. The canonical representation
        for zero is 0.0E0
        http://www.w3.org/TR/xmlschema-2/#double

        :type  o: :class:`decimal.Decimal`
        :param o: Decimal object to be canonicalized
        :rtype: :class:`str`
        :return: Canonical string representation of the decimal
        """
        # NaN and INF must not be used
        if o.is_infinite() or o.is_nan():
            msg = message_factory.get_message(
                'vapi.decimal.canonicalization')
            logger.debug(msg)
            raise CoreException(msg)

        str_val = str(o)

        # Extract sign of mantissa
        neg_sign = ''
        if str_val.startswith('-'):
            neg_sign = '-'
            str_val = str_val[1:]

        # Extract mantissa and exponent
        mantissa, exponent = None, None
        if 'E' in str_val:
            mantissa, exponent = str_val.split('E')
        else:
            mantissa = str_val

        # decimal class uses context objects that governs precision
        # for arithmetic operations. Setting the precision to the
        # length of mantissa string. To canonicalize the mantissa,
        # we do a division operation, precision maybe lost if we
        # don't set .prec
        decimal.getcontext().prec = len(mantissa)

        mantissa = decimal.Decimal(mantissa)
        exponent = int(exponent) if exponent is not None else 0

        # There MUST be a single non zero digit on the left of the decimal point
        # (unless a zero is represented)
        num_digits = 0
        if mantissa:
            num_digits = int(math.log10(mantissa))
            exponent = exponent + num_digits

            if num_digits < 0:
                # If the number is of the form 0.[0]+[1-9]+
                num_digits *= -1
                # since there MUST be a single non zero digit on the left of decimal
                # point, we have to multiple by an extra 10
                num_digits += 1
                exponent -= 1
                mantissa = mantissa * int(math.pow(10, num_digits))
            else:
                # If the number is of the form [1-9]+.[0-9]+
                mantissa = mantissa / int(math.pow(10, num_digits))

            if mantissa < 1:
                # If the original number is of the form 0.[1-9]+ then, num_digits
                # would have been 0, multile by extra 10 to get it to
                # canonical form
                mantissa *= 10
                exponent -= 1

        # There MUST be at least single digit on the right of the decimal point
        mantissa = str(mantissa)
        if '.' not in mantissa:
            mantissa = '%s.0' % mantissa

        # If there are any trailing zeros, strip them off
        left, right = mantissa.split('.')
        first = right[0]
        remaining = right[1:].rstrip('0')
        mantissa = '%s.%s%s' % (left, first, remaining)

        return '%s%sE%s' % (neg_sign, mantissa, exponent)

    class DecimalEncoder(json.JSONEncoder):
        """
        Class that adds capability of encoding decimal
        in JSON
        """
        # In 2.x version of json library, _iterencode should be overriden
        # to have special encoding for objects
        def _iterencode(self, o, markers=None):
            """
            Overriding the decimal encoding for the default encoder
            """
            if isinstance(o, decimal.Decimal):
                return (str(o) for o in [o])
            return super(DecimalEncoder, self)._iterencode(o, markers)  # pylint: disable=W0212

        # In 3.x version of json library, default() should be overrriden
        # to have special encoding for objects
        def default(self, o):  # pylint: disable=E0202
            if isinstance(o, decimal.Decimal):
                return (str(o) for o in [o])
            return json.JSONEncoder.default(self, o)

### Navigation

- index
- modules |
- vSphere Automation SDK for Python 6.5.0 documentation »
- Module code »