Source code for

Serializer vAPI data values to clean (human readable/writable) json documents

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

import decimal
import logging
import six
import base64
    import simplejson as json
except ImportError:
    import json

from import (
    StructValue, ErrorValue, ListValue, OptionalValue, VoidValue, StringValue,
    BooleanValue, IntegerValue, DoubleValue, BlobValue, SecretValue)
from vmware.vapi.lib.jsonlib import canonicalize_double

logger = logging.getLogger(__name__)

[docs]class DataValueToJSONEncoder(json.JSONEncoder): """ Custon JSON encoder that converts vAPI runtime values directly into JSON string representation. """ # Even though __init__ is called below, pylint throws a warning # that base class __init__ is not called (W0231) def __init__(self, *args, **kwargs): # pylint: disable=W0231 self._dispatch_map = { list: self.visit_list, decimal.Decimal: canonicalize_double, StructValue: self.visit_struct_value, ErrorValue: self.visit_struct_value, ListValue: self.visit_list, OptionalValue: self.visit_optional_value, DoubleValue: self.visit_double_value, IntegerValue: self.visit_primitive_value, BooleanValue: self.visit_primitive_value, StringValue: self.visit_primitive_value, VoidValue: self.visit_primitive_value, BlobValue: self.visit_blob_value, SecretValue: self.visit_primitive_value, } json.JSONEncoder.__init__(self, *args, **kwargs) # # Even though the recommended way to subclass JSONEncoder is by overriding # "default", encode is being used as it will preserve the computed JSON # string literal for decimals. If we use default, the value returned by # canonicalize_double will be wrapped inside double quotes in the final # JSON message. #
[docs] def encode(self, value): """ Encode a given vAPI runtime object :type value: :class:`object` :param value: vAPI runtime object :rtype: :class:`str` :return: JSON string """ return self._dispatch_map.get(type(value), self.visit_default)(value)
[docs] def visit_struct_value(self, value): """ Visit a StructValue object :type value: :class:`` :param value: Struct value object :rtype: :class:`str` :return: JSON string """ items = {} for field_name, field_value in value.get_fields(): # Omit unset optional values if isinstance(field_value, OptionalValue) and not field_value.is_set(): continue items[field_name] = field_value items = ['"%s":%s' % (k, self.encode(v)) for k, v in six.iteritems(items)] return '{%s}' % (','.join(items))
[docs] def visit_list(self, value): """ Visit a ListValue object :type value: :class:`` :param value: List value object :rtype: :class:`str` :return: JSON string """ string = ','.join([self.encode(item) for item in value]) return '[%s]' % string
[docs] def visit_optional_value(self, value): """ Visit a OptionalValue object :type value: :class:`` :param value: Optional value object :rtype: :class:`str` :return: JSON string """ if value.is_set(): return '%s' % self.encode(value.value) else: return 'null'
[docs] def visit_double_value(value): """ Visit a DoubleValue object :type value: :class:`` :param value: Double value object :rtype: :class:`str` :return: JSON string """ return canonicalize_double(value.value)
[docs] def visit_primitive_value(self, value): """ Visit one of StringValue, IntegerValue, BooleanValue or VoidValue :type value: :class:`` (or) :class:`` (or) :class:`` (or) :class:`` (or) :param value: StringValue, IntegerValue, BooleanValue or VoidValue object :rtype: :class:`str` :return: JSON string """ return json.JSONEncoder.encode(self, value.value)
[docs] def visit_blob_value(self, value): """ Visit BlobValue :type value: :class:`` :param value: BlobValue object :rtype: :class:`str` :return: JSON string """ if six.PY3 and isinstance(value.value, str): # For Python3 we need to convert str to byte string for b64encode data_value = base64.b64encode(six.b(value.value)) else: data_value = base64.b64encode(value.value) return json.JSONEncoder.encode(self, data_value)
[docs] def visit_default(self, value): """ This is the default visit method if the type of the input value does not match any type in the keys present in dispatch map. :type value: :class:`object` :param value: Python object :rtype: :class:`str` :return: JSON string """ return json.JSONEncoder.encode(self, value)
[docs]class DataValueConverter(object): """ Converter class that converts values from vAPI DataValue to clean JSON objects. """ @staticmethod
[docs] def convert_to_json(data_value): """ Convert the given data value to a JSON string representation :type data_value: :class:`` :param data_value: Data value to be converted :rtype: :class:`str` :return: JSON representation of the data value """ return json.dumps(data_value, check_circular=False, separators=(',', ':'), cls=DataValueToJSONEncoder)