| # -*- coding: utf-8 -*- |
| # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """A convenience class for objects which can be converted to JSON.""" |
| |
| from __future__ import print_function |
| |
| _BUILTINS = (str, int, float, bool, type(None)) |
| |
| |
| class Structured(object): |
| """An object with a approved set of public properties (.VISIBLE_KEYS)""" |
| |
| def _Keys(self): |
| seen = set() |
| for cls in type(self).mro(): |
| for k in getattr(cls, 'VISIBLE_KEYS', ()): |
| if k not in seen: |
| yield k |
| seen.add(k) |
| |
| def ToDict(self): |
| return ToStructure(self) |
| |
| |
| def ToStructure(value): |
| """Makes an object JSON-encodable. |
| |
| Args: |
| value: An object which can be converted to a JSON-encodable object. |
| |
| Returns: |
| An object which is legal to pass into json.dumps(obj), namely |
| type JSONEncodable = ( |
| str | int | float | NoneType | dict<str, JSONEncodable> | |
| list<JSONEncodable> |
| |
| Raises: |
| StackOverflow if the object has circular references (parent -> child -> |
| parent). |
| """ |
| if isinstance(value, _BUILTINS): |
| return value |
| |
| elif hasattr(value, '_Keys'): |
| ret = {} |
| for k in value._Keys(): # pylint: disable=protected-access |
| v = ToStructure(getattr(value, k, None)) |
| if v is not None: |
| ret[k] = v |
| return ret |
| |
| elif isinstance(value, dict): |
| ret = {} |
| for k, v in value.items(): |
| v = ToStructure(v) |
| if v is not None: |
| ret[k] = v |
| return ret |
| |
| else: |
| try: |
| iterator = iter(value) |
| except TypeError: |
| return None |
| |
| ret = [] |
| for element in iterator: |
| v = ToStructure(element) |
| if v is not None: |
| ret.append(v) |
| |
| return ret |