Переопределение вложенной JSON-кодировки унаследованных объектов, поддерживающих по умолчанию, таких как dict, list

Я создал некоторые собственные классы, которые подклассифицированы из словаря, чтобы действовать как они. Однако, когда я хочу кодировать их в JSON (используя Python), я хочу, чтобы они были сериализованы таким образом, что я могу их декодировать обратно к исходным объектам, а не к dict.

Поэтому я хочу поддерживать вложенные объекты моих собственных классов (которые унаследованы от 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)

And:

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)

It works in general, yet not when they are nested or the first object getting dumped is not of those types. Thus this only works when the input object is of that type. Yet not when it's nested.

I'm not sure how to encode this JSON recursively so all nested/contained instances are encoded according to the same rules.

I thought it would be easier to the use the JSONEncoder's default method (as that gets called whenever an object is of an unsupported type.) Yet since my objects are inherited from dict they get parsed to dictionaries instead of being process by the 'default' method.

One Solution collect form web for “Переопределение вложенной JSON-кодировки унаследованных объектов, поддерживающих по умолчанию, таких как dict, list”

I ended up doing the following.

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

I assume this is not the best way to do this, yet I'm sharing it here to see if anyone else can tell me what I'm doing wrong and show me the best way to do this!

Note that I'm using nested tuples instead of a dictionary because I wanted to keep the order they were listed so I could – in this example – override ShaderInfo becoming _NodeInfo if ShaderInfo was an object that inherited from NodeInfo.

My decoder is set up to do something along the lines of (simplified and part of code):

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

The nodeInfoDecode methods gets the entered dictionary and uses it to initialize the values/attributes of the NodeInfo object that gets created and returned.

More info:

Also see my answer on How to change json encoding behaviour for serializable python object?

Python - лучший язык программирования в мире.