1# -*- coding: utf-8 -*- 2# Copyright 2017 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""A convenience class for objects which can be converted to JSON.""" 7 8from __future__ import print_function 9 10_BUILTINS = (str, int, float, bool, type(None)) 11 12 13class Structured(object): 14 """An object with a approved set of public properties (.VISIBLE_KEYS)""" 15 16 def _Keys(self): 17 seen = set() 18 for cls in type(self).mro(): 19 for k in getattr(cls, 'VISIBLE_KEYS', ()): 20 if k not in seen: 21 yield k 22 seen.add(k) 23 24 def ToDict(self): 25 return ToStructure(self) 26 27 28def ToStructure(value): 29 """Makes an object JSON-encodable. 30 31 Args: 32 value: An object which can be converted to a JSON-encodable object. 33 34 Returns: 35 An object which is legal to pass into json.dumps(obj), namely 36 type JSONEncodable = ( 37 str | int | float | NoneType | dict<str, JSONEncodable> | 38 list<JSONEncodable> 39 40 Raises: 41 StackOverflow if the object has circular references (parent -> child -> 42 parent). 43 """ 44 if isinstance(value, _BUILTINS): 45 return value 46 47 elif hasattr(value, '_Keys'): 48 ret = {} 49 for k in value._Keys(): # pylint: disable=protected-access 50 v = ToStructure(getattr(value, k, None)) 51 if v is not None: 52 ret[k] = v 53 return ret 54 55 elif isinstance(value, dict): 56 ret = {} 57 for k, v in value.items(): 58 v = ToStructure(v) 59 if v is not None: 60 ret[k] = v 61 return ret 62 63 else: 64 try: 65 iterator = iter(value) 66 except TypeError: 67 return None 68 69 ret = [] 70 for element in iterator: 71 v = ToStructure(element) 72 if v is not None: 73 ret.append(v) 74 75 return ret