python - 在编写 JSON 时防止循环的 pythonic 方法是什么?

标签 python json serialization

我有两个类 A 和 B,每个类都在列表中存储对另一个类的对象的引用:

class A:
    def __init__(self,name):
        self.name = name
        self.my_Bs = []
    def registerB(self,b):
        self.my_Bs.append(b)

class B:
    def __init__(self,name):
        self.name = name
        self.my_As = []
    def registerA(self,a):
        self.my_As.append(a)

现在,我的应用构建了两个列表,一个是 A 的对象,一个是 B 的对象,具有交叉引用。

# a list of As, a list of Bs
list_of_As = [A('firstA'), A('secondA')]
list_of_Bs = [B('firstB'), B('secondB')]
# example of one cross-reference
list_of_As[0].registerB(list_of_Bs[1])
list_of_Bs[1].registerA(list_of_As[0])

显然,如果我在 list_of_... 上调用 json.dumps(),我会收到循环引用错误。

为了避免这个问题,我想做的是转储带有元素 name 属性列表 的 JSON 而不是 对象本身 的列表:

# This is what I want to obtain for
# the JSON for list_of_As
[
    {'name' : 'firstA', 'my_Bs': ['secondB']},
    {'name' : 'secondA', 'my_Bs': []}
]

我能想到的唯一方法是在每个类中维护一个额外的字符串列表(分别是 my_Bs_namesmy_As_names)并使用 JSONEncoder如下:

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, 'A'):
            return { # filter out the list of B objects
                k: v for k, v in obj.__dict__.items() if k != 'my_Bs'
            }
        if isinstance(obj, 'B'):
            return { # filter out the list of A objects
                k: v for k, v in obj.__dict__.items() if k != 'my_As'
            }
        return super(MyEncoder, self).default(obj)

# Use the custom encoder to dump JSON for list_of_As
print json.dumps(list_of_As, cls=MyEncoder)

如果我没记错的话,我会得到如下结果:

# This is what I obtain for
# the JSON for list_of_As with the code above
[
    {'name' : 'firstA', 'my_Bs_names': ['secondB']},
    {'name' : 'secondA', 'my_Bs_names': []}
]

有没有更优雅的方法来获得这个结果?例如,不需要任何额外的字符串列表?

最佳答案

防止循环引用错误的通用JSONEncoder

以下编码器类 MyEncoder 对嵌套对象执行递归编码,直到检测到循环引用,返回其“名称”属性而不是对象本身。

import json
class MyEncoder(json.JSONEncoder):
    def __init__(self, *args, **argv):
        super().__init__(*args, **argv)
        self.proc_objs = []
    def default(self, obj):
        if isinstance(obj,(A,B)):
            if obj in self.proc_objs:
                return obj.name # short circle the object dumping
            self.proc_objs.append(obj)
            return obj.__dict__
        return obj

json.dumps(list_of_As, cls=MyEncoder, check_circular=False, indent=2)

输出:

[
  { "name": "firstA",
    "my_Bs": [
      { "name": "secondB",
        "my_As": [ "firstA" ]
      }
    ]
  },
  { "name": "secondA", "my_Bs": [] }
]

使用自定义toJSON方法

您可以实现 serializer method在你的类里面。

class JSONable:
    def toJSON(self):
        d = dict()
        for k,v in self.__dict__.items():
            # save a list of "name"s of the objects in "my_As" or "my_Bs"
            d[k] = [o.name for o in v] if isinstance(v, list) else v
        return d

class A(JSONable):
    def __init__(self,name):
        self.name = name
        self.my_Bs = []
    def register(self,b):
        self.my_Bs.append(b)

class B(JSONable):
    def __init__(self,name):
        self.name = name
        self.my_As = []
    def register(self,a):
        self.my_As.append(a)

json.dumps(list_of_As, default=lambda x: x.toJSON(), indent=2)

输出:

[
  { "name":  "firstA",  "my_Bs": [  "secondB" ] },
  { "name":  "secondA", "my_Bs": [] }
]

关于python - 在编写 JSON 时防止循环的 pythonic 方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54873059/

相关文章:

.net - 从字符串构造 C# Type 以进行序列化/反序列化的最佳方法

python - 记录抽象基类的属性?

python - 将 JSON 转换为 python 数据帧

mysql - 将 JSON 数据逐行插入 SQL 表中

c# - 如何在 Unity Inspector 中创建多维数组?

java - 注解会影响 Java 默认序列化吗?

python - 我可以更改 Spark 数据框中列的可空性吗?

python - 在 python 中使用 coreNLP 的斯坦福类型依赖项

python - 如何使 matplotlib/pylab 中的 X Axis 不自动对值进行排序?

c++ - Boost 将对象序列化为 json