覆盖继承的默认支持对象(如 dict、list)的嵌套 JSON 编码

Posted

技术标签:

【中文标题】覆盖继承的默认支持对象(如 dict、list)的嵌套 JSON 编码【英文标题】:Overriding nested JSON encoding of inherited default supported objects like dict, list 【发布时间】:2013-04-27 23:50:45 【问题描述】:

我已经设置了一些我自己的类,这些类是从字典中继承的,以像它们一样工作。然而,当我想将它们编码为 JSON(使用 Python)时,我希望它们以一种我可以将它们解码回原始对象而不是字典的方式进行序列化。

所以我想支持我自己的类的嵌套对象(继承自 dict)。

我尝试过类似的东西:

class ShadingInfoEncoder(json.JSONEncoder):
    def encode(self, o):
        if type(o).__name__ == "NodeInfo":
            return '"_NodeInfo": ' + super(ShadingInfoEncoder, self).encode(o) + ''
        elif type(o).__name__ == "ShapeInfo":
            return '"_ShapeInfo": ' + super(ShadingInfoEncoder, self).encode(o) + ''
        elif type(o).__name__ == "ShaderInfo":
            return '"_ShaderInfo": ' + super(ShadingInfoEncoder, self).encode(o) + ''

        return super(ShadingInfoEncoder, self).encode(o)

还有:

class ShadingInfoEncoder(json.JSONEncoder):
    def encode(self, o):
        if isinstance(o, NodeInfo):
            return '"_NodeInfo": ' + super(ShadingInfoEncoder, self).encode(o) + ''
        elif isinstance(o, ShapeInfo):
            return '"_ShapeInfo": ' + super(ShadingInfoEncoder, self).encode(o) + ''
        elif isinstance(o, ShaderInfo):
            return '"_ShaderInfo": ' + super(ShadingInfoEncoder, self).encode(o) + ''

        return super(ShadingInfoEncoder, self).encode(o)

它通常可以工作,但当它们嵌套或转储的第一个对象不是这些类型时则不行。因此,这仅在输入对象属于该类型时才有效。然而,当它嵌套时不是。

我不确定如何递归地编码这个 JSON,所以所有嵌套/包含的实例都根据相同的规则进行编码。

我认为使用 JSONEncoder 的默认方法会更容易(因为只要对象的类型不受支持,就会调用该方法。)然而,由于我的对象是从 dict 继承的,它们被解析为字典而不是由“默认”方法。

【问题讨论】:

【参考方案1】:

我最终做了以下事情。

class ShadingInfoEncoder(json.JSONEncoder):
    def _iterencode(self, o, markers=None):
        jsonPlaceholderNames = (("_ShaderInfo", ShaderInfo),
                            ("_ShapeInfo", ShapeInfo),
                            ("_NodeInfo", NodeInfo))
        for jsonPlaceholderName, cls in customIterEncode:
            if isinstance(o, cls):
                yield '"' + jsonPlaceholderName+ '": '
                for chunk in super(ShadingInfoEncoder, self)._iterencode(o, markers):
                    yield chunk
                yield ''
                break
        else:
            for chunk in super(ShadingInfoEncoder, self)._iterencode(o, markers):
                yield chunk

我认为这不是最好的方法,但我在这里分享它,看看是否有其他人可以告诉我我做错了什么并告诉我最好的方法!

请注意,我使用嵌套元组而不是字典,因为我想保持它们列出的顺序,因此我可以 - 在本示例中 - 如果 ShaderInfo 是从 NodeInfo 继承的对象,则将 ShaderInfo 覆盖为 _NodeInfo。

我的解码器设置为执行以下操作(简化和部分代码):

class ShadingInfoDecoder(json.JSONDecoder):
    def decode(self, obj):
        obj = super(ShadingInfoDecoder,self).decode(s)
        if isinstance(obj, dict):
            decoders = [("_set",self.setDecode),
                        ("_NodeInfo", self.nodeInfoDecode),
                        ("_ShapeInfo", self.shapeInfoDecode),
                        ("_ShaderInfo", self.shaderInfoDecode)]
            for placeholder, decoder in decoders:
                if placeholder in obj:
                    return decoder(obj[placeholder])
                else:
                    for k in obj:
                        obj[k] = self.recursiveDecode(obj[k])
        elif isinstance(obj, list):
            for x in range(len(obj)):
                obj[x] = self.recursiveDecode(obj[x])

        return obj

    def setDecode(self, v):
        return set(v)

    def nodeInfoDecode(self, v):
        o = NodeInfo()
        o.update(self.recursiveDecode(v))
        return o

    def shapeInfoDecode(self, v):
        o = ShapeInfo()
        o.update(self.recursiveDecode(v))
        return o

    def shaderInfoDecode(self, v):
        o = ShaderInfo()
        o.update(self.recursiveDecode(v))
        return o

nodeInfoDecode 方法获取输入的字典并使用它来初始化创建和返回的 NodeInfo 对象的值/属性。

更多信息:

也可以在How to change json encoding behaviour for serializable python object?上查看我的回答

【讨论】:

以上是关于覆盖继承的默认支持对象(如 dict、list)的嵌套 JSON 编码的主要内容,如果未能解决你的问题,请参考以下文章

如何为对象创建默认值? PHP

Django入门二之模板语法

__slot__

Java继承

day07 继承 覆盖

覆盖 RSA 对象中的默认算法以支持 OaepSHA256 填充