Search code examples
pythonjsonstringdictionarytruncate

Truncate long strings inside json in python


Given some json

[
    {
        "default_value": "True",
        "feature_name": "feature_1",
        "active_on": "remote_1,remote_2,remote_3,remote_4,remote_5,remote_6,remote_7"
    },
    {
        "feature_name": "special_super_duper_feature_wooooooooot",
        "active_on": "remote_2"
    }
]

how do I truncate values longer than, say, 20 chars:

[
    {
        "default_value": "True",
        "feature_name": "feature_1",
        "active_on": "remote_1,remote_2..."
    },
    {
        "feature_name": "special_super_dup...",
        "active_on": "remote_2"
    }
]

as generically as possible?

EDIT: Here's a more generic example to fit:

[
    {
        "a": {"b": "c"},
        "d": "e"
    },
    {
        "a": [{"b": "dugin-walrus-blowing-up-the-view-and-ruining-page-frame"}]
    }
]

The endgame here is to make "pretty-print" for arbitrary json. I'm wondering whether there's a nice way to do that using only standard library.


Solution

  • Here's my take on it:

    import collections
    ...
    
    def truncate_strings(obj: collections.abc.Iterable, truncate: int, suffix: str = "...") -> int:
        """
        @param obj: generic iterable  - object to truncate. Implemented for dicts and lists. 
                                        Extensible by adding new types to isinstance.
        @param truncate: int          - total str length to be set. value 0 to disable.
        @param suffix: str [Optional] - the truncation string suffix
    
        @returns count: int           - number of strings truncated 
        """
    
        if not truncate or not obj or isinstance(obj, str):
            return 0
        count = 0
        if isinstance(obj, dict):
            for key_ in obj.keys():
                if not key_:
                    return 0
                if isinstance(obj.get(key_), str):
                    if len(obj[key_]) > truncate - len(suffix):
                        count += 1
                        obj[key_] = obj[key_][:truncate - len(suffix)] + suffix
                else:
                    count += truncate_strings(key_, truncate, suffix)
        elif isinstance(obj, collections.abc.Iterable):
            for item in obj:
                count += truncate_strings(item, truncate, suffix)
        return count
    

    Note this func is recursive on iterables, so a long list will kill your call stack. Should work on reasonable size jsons though (tested on 1k items json array)