OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

What is JSONEncoder c_make_encoder?

  • Thread starter Thread starter Nuno
  • Start date Start date
N

Nuno

Guest
I LOVE the python module json, and I love json.dumps(); however, I hate the fact that you need to pass additional arguments to it if you want to extend its behaviour.

I am strong believer that classes should take care of themselves, so I created the class Serialisable that goes like this:

Code:
class Serialisable(dict[str, Any]):
    def __len__(self) -> int:
        return len(self.items())
    def __str__(self) -> str:
        return str(dict(self.items()))
    def __getitem__(self, __key: str) -> Any: 
        return getattr(self, __key)
    def __setitem__(self, __key: str, __value: Any) -> None:
        vars(self)[__key] = __value
    def __delitem__(self, __key: str) -> None:
        variable = getattr(self, __key)
        del variable
    def __contains__(self, __key: str) -> bool:
        return __key in vars(self)
    def __iter__(self) -> Iterator[str]:
        return self.keys().__iter__()
    def keys(self) -> list[str]:
        return [keys for keys, _ in self.items()]
    def values(self) -> list[Any]:
        return [values for _, values in self.items()]
    def items(self) -> list[tuple[str, Any]]:
        result: list[tuple[str, Any]] = []
        for name, value in vars(self).items():
            variable = getattr(self, name)
            if isinstance(variable, Serialisable): pass
            elif 'builtin' not in variable.__class__.__module__: continue
            result.append((name, value))
        return result

Basically, if you have a sub class of Serialisable, all of the instance variables (that are serialisable) will appear as part of a dictionary:

Code:
@dataclass
class subSerial(Serialisable):
    boolean: bool = True
    real: float = -1.0

@dataclass
class NonSerial:
    string: str = 'Miguel'

class mainSerial(Serialisable):
    def __init__(self) -> None:
        self.string: str = 'Calgary'
        self.integer: int = 53
        self.subClass: subSerial = subSerial()
        self.name: dict[str, str] = {'King': 'Charles'}
        self.discard: NonSerial = NonSerial()

print(f"{serial}")
{'string': 'Calgary', 'integer': 53, 'subClass': subSerial(boolean=True, real=-1.0), 'name': {'King': 'Charles'}}

If I run this code print(f"JSON indent = {json.dumps(serial, sort_keys=True, indent=4)}")

I get this pretty result:

Code:
JSON indent = {
    "integer": 28,
    "name": {
        "King": "Charles"
    },
    "string": "Calgary",
    "subClass": {
        "boolean": true,
        "real": -1.0
    }
}

If I remove the indent=4, I get this:

Code:
JSON NON indent = {}

Long story short, it all boils down to how Python has implemented json.dumps() and this piece of code on encoder.py

Code:
        if (_one_shot and c_make_encoder is not None
                and self.indent is None):
            _iterencode = c_make_encoder(
                markers, self.default, _encoder, self.indent,
                self.key_separator, self.item_separator, self.sort_keys,
                self.skipkeys, self.allow_nan)
        else:
            _iterencode = _make_iterencode(
                markers, self.default, _encoder, self.indent, floatstr,
                self.key_separator, self.item_separator, self.sort_keys,
                self.skipkeys, _one_shot)
        return _iterencode(o, 0)

c_make_encoder seems to be an external library function (written in C?) that apparently doesn't understand how custom dictionaries work!

So, here is(are) my question(s):

  1. Do you know what I am missing on my Serialisable class to make it act a like a full dictionary and json.dumps()serialises it properly? alternatively
  2. Do you know where can I find the code of c_make_encoder?

(I have found this very useless code on _json.pyi)

Code:
@final
class make_encoder:
    @property
    def sort_keys(self) -> bool: ...
    @property
    def skipkeys(self) -> bool: ...
    @property
    def key_separator(self) -> str: ...
[...]
<p>I LOVE the python module json, and I love json.dumps(); however, I hate the fact that you need to pass additional arguments to it if you want to extend its behaviour.</p>
<p>I am strong believer that classes should take care of themselves, so I created the class <code>Serialisable</code> that goes like this:</p>
<pre><code>class Serialisable(dict[str, Any]):
def __len__(self) -> int:
return len(self.items())
def __str__(self) -> str:
return str(dict(self.items()))
def __getitem__(self, __key: str) -> Any:
return getattr(self, __key)
def __setitem__(self, __key: str, __value: Any) -> None:
vars(self)[__key] = __value
def __delitem__(self, __key: str) -> None:
variable = getattr(self, __key)
del variable
def __contains__(self, __key: str) -> bool:
return __key in vars(self)
def __iter__(self) -> Iterator[str]:
return self.keys().__iter__()
def keys(self) -> list[str]:
return [keys for keys, _ in self.items()]
def values(self) -> list[Any]:
return [values for _, values in self.items()]
def items(self) -> list[tuple[str, Any]]:
result: list[tuple[str, Any]] = []
for name, value in vars(self).items():
variable = getattr(self, name)
if isinstance(variable, Serialisable): pass
elif 'builtin' not in variable.__class__.__module__: continue
result.append((name, value))
return result
</code></pre>
<p>Basically, if you have a sub class of <code>Serialisable</code>, all of the instance variables (that are serialisable) will appear as part of a dictionary:</p>
<pre><code>@dataclass
class subSerial(Serialisable):
boolean: bool = True
real: float = -1.0

@dataclass
class NonSerial:
string: str = 'Miguel'

class mainSerial(Serialisable):
def __init__(self) -> None:
self.string: str = 'Calgary'
self.integer: int = 53
self.subClass: subSerial = subSerial()
self.name: dict[str, str] = {'King': 'Charles'}
self.discard: NonSerial = NonSerial()

print(f"{serial}")
</code></pre>
<blockquote>
<p>{'string': 'Calgary', 'integer': 53, 'subClass': subSerial(boolean=True, real=-1.0), 'name': {'King': 'Charles'}}</p>
</blockquote>
<p>If I run this code <code>print(f"JSON indent = {json.dumps(serial, sort_keys=True, indent=4)}")</code></p>
<p>I get this pretty result:</p>
<pre><code>JSON indent = {
"integer": 28,
"name": {
"King": "Charles"
},
"string": "Calgary",
"subClass": {
"boolean": true,
"real": -1.0
}
}
</code></pre>
<p>If I remove the <code>indent=4</code>, I get this:</p>
<pre><code>JSON NON indent = {}
</code></pre>
<p>Long story short, it all boils down to how Python has implemented <code>json.dumps()</code> and this piece of code on <code>encoder.py</code></p>
<pre><code> if (_one_shot and c_make_encoder is not None
and self.indent is None):
_iterencode = c_make_encoder(
markers, self.default, _encoder, self.indent,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, self.allow_nan)
else:
_iterencode = _make_iterencode(
markers, self.default, _encoder, self.indent, floatstr,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, _one_shot)
return _iterencode(o, 0)
</code></pre>
<p><code>c_make_encoder</code> <strong>seems</strong> to be an external library function (written in C?) that apparently doesn't understand how custom dictionaries work!</p>
<p>So, here is(are) my question(s):</p>
<ol>
<li>Do you know what I am missing on my <code>Serialisable</code> class to make it act a like a full dictionary and <code>json.dumps()</code>serialises it properly? <strong>alternatively</strong></li>
<li>Do you know where can I find the code of c_make_encoder?</li>
</ol>
<p>(I have found this very useless code on <code>_json.pyi</code>)</p>
<pre><code>@final
class make_encoder:
@property
def sort_keys(self) -> bool: ...
@property
def skipkeys(self) -> bool: ...
@property
def key_separator(self) -> str: ...
[...]
</code></pre>
 
Top