0

I'm implementing an API (proxy) that accepts incoming JSON POST data in Flask. I need to process this JSON and after that send it on to a backend API, which was written in another language.

The JSON data will be sent by end-users, and they are used to sending this JSON data case-insensitive. This means that the incoming JSON data will sometimes have uppercase keys/nodes, sometimes lowercase, and sometimes maybe camelcase or pascalcase.

I'm using Flasks request.json to get the data from the request. It is parsed into a Python object, but this object will have case-sensitive keys and values. These will also be nested.

A specific example of how I currently get data is:

data['ORDERS']['authentication']['AccountToken']

But my users might POST:

{
    "Orders": {
        "Authentication": {
            "AccountToken": "C3485D7B" 
        },
    ...

Is there a way to get data['ORDERS']['authentication']['AccountToken'] in such a way that the complete path to that value is case-insensitive? I understand I can check for each part of the path case-insensitive separately, but that requires a lot of overhead code to get to the right child-nodes.

I saw other solutions: Case insensitive dictionary

I have also tried using CaseInsensitiveDict from the requests library like this: data = CaseInsensitiveDict(request.json), but that only makes the first level of the object case insensitive actually.

The problem with these solutions is that they deal with dicts, while the JSON data is a dict of objects that can be lists or other objects. The solutions provided don't work recursively or only on Dicts.

Any help is appreciated.

Erik Oosterwaal
  • 4,272
  • 3
  • 44
  • 64
  • 1
    can you convert the text of the request and apply `lower()` on it prior to calling `json.loads()`? – JonSG Apr 12 '23 at 14:11
  • Does this answer your question? [Case insensitive dictionary](https://stackoverflow.com/questions/2082152/case-insensitive-dictionary) and/or https://stackoverflow.com/questions/50563844/access-python-dictionary-with-case-insensitive-string – JonSG Apr 12 '23 at 14:13
  • 1
    @JonSG it is a bytestream coming in that gets converted to objects, so there is no string to use lower() on. The other answers are similar (or the same) as I have already tried with using CaseInsensitiveDict(). It doesn't work recursively on the child nodes. – Erik Oosterwaal Apr 12 '23 at 14:28

2 Answers2

2

Do you have to keep the case that the users send you? If not, it's always a good idea to sanitise your input to some standard format, rather than anticipating everything you could be sent. Rather than checking your path case-insensitively, you could convert all your keys to plain lowercase on input, eg:

def make_all_keys_lowercase(d: dict) -> None:
    for k, v in list(d.items()):
        if type(v) == dict:
            make_all_keys_lowercase(d[k])
        d[k.lower()] = d.pop(k)

Which doesn't require a lot of boilerplate and lets you keep the same path internally.

DavP
  • 21
  • 5
1

This function outputs a case insensitive dict, creating it recursively for every entry:

from requests.structures import CaseInsensitiveDict

def case_insensitive_copy(data):
    if not isinstance(data, dict):
       return data
    temp_dict = {}
    for key, value in data.items():
       temp_dict[key] = case_insensitive_copy(value)
    return CaseInsensitiveDict(temp_dict)

d = { "Orders": {
    "Authentication": {
        "AccountToken": "C3485D7B"
         }
      }
    }

d = case_insensitive_copy(d)
print(d['ORDERS']['authentication']['AccountToken'])
>>> C3485D7B
vighub
  • 173
  • 1
  • 6