"""
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
try:
import simplejson as json
except ImportError:
import json
from vmware.vapi.data.value 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:`vmware.vapi.data.value.StructValue`
: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:`vmware.vapi.data.value.ListValue`
: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:`vmware.vapi.data.value.OptionalValue`
:param value: Optional value object
:rtype: :class:`str`
:return: JSON string
"""
if value.is_set():
return '%s' % self.encode(value.value)
else:
return 'null'
@staticmethod
[docs] def visit_double_value(value):
"""
Visit a DoubleValue object
:type value: :class:`vmware.vapi.data.value.DoubleValue`
: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:`vmware.vapi.data.value.StringValue` (or)
:class:`vmware.vapi.data.value.IntegerValue` (or)
:class:`vmware.vapi.data.value.BooleanValue` (or)
:class:`vmware.vapi.data.value.VoidValue` (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:`vmware.vapi.data.value.BlobValue`
: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:`vmware.vapi.data.value.DataValue`
: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)