MortenB
MortenB

Reputation: 3567

python json.JSONEncoder encoder do not match str values but other objects

Instead of traversing entire structure I hoped to use json.JSONEncoder, but I'm unable to get it to trigger on strings:

#!/usr/bin/env python3
import json
import datetime
class StringShortener(json.JSONEncoder):
    def default(self, obj):
        strl = 10
        if not isinstance(obj, (int,float)) and len(str(obj))>strl:
            return str(obj)[:strl] + f"<<cut at {strl}>>"
        return obj
mystruct = {
    "dummy": "foobar just being more than 10char long",
    "dt": datetime.datetime.now() 
}
# only shortening the td object
print(json.dumps(mystruct, cls=StringShortener))

Gives:

{"dummy": "foobar just being more than 10char long", "dt": "2022-09-22<<cut at 10>>"}

But key 'dummy's value also match the stringShortener:

>>> obj = "foobar just being more than 10char long"
>>> bool(not isinstance(obj, (int,float)) and len(str(obj))>10)   
True
>>> str(obj)[:10] + f"<<cut at 10>>"       
'foobar jus<<cut at 10>>'

How can this be fixed so str types also get shortened?

Upvotes: 1

Views: 295

Answers (1)

Philipp
Philipp

Reputation: 51

Overriding the encode function should do it.

    import json
    import datetime
    
    class StringShortener(json.JSONEncoder):
        def default(self, obj):
            strl = 10
            if not isinstance(obj, (int,float)) and len(str(obj))>strl:
                return str(obj)[:strl] + f"<<cut at {strl}>>"
            return obj
    
        # just override the encode function in the same way as the default function
        def encode(self, o):
            strl = 10
            # This is for extremely simple cases and benchmarks.s
            if isinstance(o, str):
                if self.ensure_ascii:
                    return o[:strl]
                else:
                    return o
           
            # cut the strings if they are too long
            for key, value in o.items():
                if isinstance(value, str):
                    o[key] = value[:strl] + f"<<cut at {strl}>>"
     
            # This doesn't pass the iterator directly to ''.join() because the
            # exceptions aren't as detailed.  The list call should be roughly
            # equivalent to the PySequence_Fast that ''.join() would do.
            chunks = self.iterencode(o, _one_shot=True)
            if not isinstance(chunks, (list, tuple)):
                chunks = list(chunks)
            return ''.join(chunks)
           
    mystruct = {
        "dummy": "foobar just being more than 10char long",
        "dt": datetime.datetime.now() 
    }
    # shortening the td and dummy object
    print(json.dumps(mystruct, cls=StringShortener))

giving:

{"dummy": "foobar jus<<cut at 10>>", "dt": "2022-09-23<<cut at 10>>"}

Upvotes: 1

Related Questions