| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| """JSON conversion utility functions.""" |
| |
| # pytype: skip-file |
| |
| from apache_beam.options.value_provider import ValueProvider |
| |
| # Protect against environments where apitools library is not available. |
| # pylint: disable=wrong-import-order, wrong-import-position |
| try: |
| from apitools.base.py import extra_types |
| except ImportError: |
| extra_types = None |
| # pylint: enable=wrong-import-order, wrong-import-position |
| |
| _MAXINT64 = (1 << 63) - 1 |
| _MININT64 = -(1 << 63) |
| |
| |
| def get_typed_value_descriptor(obj): |
| """For internal use only; no backwards-compatibility guarantees. |
| |
| Converts a basic type into a @type/value dictionary. |
| |
| Args: |
| obj: A bytes, unicode, bool, int, or float to be converted. |
| |
| Returns: |
| A dictionary containing the keys ``@type`` and ``value`` with the value for |
| the ``@type`` of appropriate type. |
| |
| Raises: |
| TypeError: if the Python object has a type that is not |
| supported. |
| """ |
| if isinstance(obj, (bytes, str)): |
| type_name = 'Text' |
| elif isinstance(obj, bool): |
| type_name = 'Boolean' |
| elif isinstance(obj, int): |
| type_name = 'Integer' |
| elif isinstance(obj, float): |
| type_name = 'Float' |
| else: |
| raise TypeError('Cannot get a type descriptor for %s.' % repr(obj)) |
| return {'@type': 'http://schema.org/%s' % type_name, 'value': obj} |
| |
| |
| def to_json_value(obj, with_type=False): |
| """For internal use only; no backwards-compatibility guarantees. |
| |
| Converts Python objects into extra_types.JsonValue objects. |
| |
| Args: |
| obj: Python object to be converted. Can be :data:`None`. |
| with_type: If true then the basic types (``bytes``, ``unicode``, ``int``, |
| ``float``, ``bool``) will be wrapped in ``@type:value`` dictionaries. |
| Otherwise the straight value is encoded into a ``JsonValue``. |
| |
| Returns: |
| A ``JsonValue`` object using ``JsonValue``, ``JsonArray`` and ``JsonObject`` |
| types for the corresponding values, lists, or dictionaries. |
| |
| Raises: |
| TypeError: if the Python object contains a type that is not |
| supported. |
| |
| The types supported are ``str``, ``bool``, ``list``, ``tuple``, ``dict``, and |
| ``None``. The Dataflow API requires JsonValue(s) in many places, and it is |
| quite convenient to be able to specify these hierarchical objects using |
| Python syntax. |
| """ |
| if obj is None: |
| return extra_types.JsonValue(is_null=True) |
| elif isinstance(obj, (list, tuple)): |
| return extra_types.JsonValue( |
| array_value=extra_types.JsonArray( |
| entries=[to_json_value(o, with_type=with_type) for o in obj])) |
| elif isinstance(obj, dict): |
| json_object = extra_types.JsonObject() |
| for k, v in obj.items(): |
| json_object.properties.append( |
| extra_types.JsonObject.Property( |
| key=k, value=to_json_value(v, with_type=with_type))) |
| return extra_types.JsonValue(object_value=json_object) |
| elif with_type: |
| return to_json_value(get_typed_value_descriptor(obj), with_type=False) |
| elif isinstance(obj, str): |
| return extra_types.JsonValue(string_value=obj) |
| elif isinstance(obj, bytes): |
| return extra_types.JsonValue(string_value=obj.decode('utf8')) |
| elif isinstance(obj, bool): |
| return extra_types.JsonValue(boolean_value=obj) |
| elif isinstance(obj, int): |
| if _MININT64 <= obj <= _MAXINT64: |
| return extra_types.JsonValue(integer_value=obj) |
| else: |
| raise TypeError('Can not encode {} as a 64-bit integer'.format(obj)) |
| elif isinstance(obj, float): |
| return extra_types.JsonValue(double_value=obj) |
| elif isinstance(obj, ValueProvider): |
| if obj.is_accessible(): |
| return to_json_value(obj.get()) |
| return extra_types.JsonValue(is_null=True) |
| else: |
| raise TypeError('Cannot convert %s to a JSON value.' % repr(obj)) |
| |
| |
| def from_json_value(v): |
| """For internal use only; no backwards-compatibility guarantees. |
| |
| Converts ``extra_types.JsonValue`` objects into Python objects. |
| |
| Args: |
| v: ``JsonValue`` object to be converted. |
| |
| Returns: |
| A Python object structured as values, lists, and dictionaries corresponding |
| to ``JsonValue``, ``JsonArray`` and ``JsonObject`` types. |
| |
| Raises: |
| TypeError: if the ``JsonValue`` object contains a type that is |
| not supported. |
| |
| The types supported are ``str``, ``bool``, ``list``, ``dict``, and ``None``. |
| The Dataflow API returns JsonValue(s) in many places and it is quite |
| convenient to be able to convert these hierarchical objects to much simpler |
| Python objects. |
| """ |
| if isinstance(v, extra_types.JsonValue): |
| if v.string_value is not None: |
| return v.string_value |
| elif v.boolean_value is not None: |
| return v.boolean_value |
| elif v.integer_value is not None: |
| return v.integer_value |
| elif v.double_value is not None: |
| return v.double_value |
| elif v.array_value is not None: |
| return from_json_value(v.array_value) |
| elif v.object_value is not None: |
| return from_json_value(v.object_value) |
| elif v.is_null: |
| return None |
| elif isinstance(v, extra_types.JsonArray): |
| return [from_json_value(e) for e in v.entries] |
| elif isinstance(v, extra_types.JsonObject): |
| return {p.key: from_json_value(p.value) for p in v.properties} |
| raise TypeError('Cannot convert %s from a JSON value.' % repr(v)) |