Я создаю заменитель токена json, который работает следующим образом:
ввод: {"greet": "hello {{name}}"}
и токены {"name": "Lorenzo"}
вывод: {"greet": "hello Lorenzo"}
Поскольку я хочу, чтобы функция работала также с вложенными диктовками/списками, я использовал рекурсию для расширения функциональности более сложных входных данных, таких как:
ввод: {"a": {"b": "x{{ppp}}z"}}
и токены {"ppp": "y"})
вывод: {"a": {"b": "xyz"}}
(другие тесты/примеры использования доступны здесь)
Даже если код работает, меня не устраивает мое текущее решение, потому что я нарушил принцип DRY.
Я уверен, что смогу сделать свой код чище: есть идеи или предложения? Вот что я пробовал:
def __replace(input_str: str, pytokens: dict):
for k, v in pytokens.items():
input_str = re.sub("{{" + k + "}}", str(v), input_str)
return input_str
def __json_replacer(pyinput, pytokens: dict):
# TODO DRY: code is doubled, code this part again
if isinstance(pyinput, str):
return __replace(pyinput, pytokens)
if isinstance(pyinput, list):
for i in range(len(pyinput)):
pyinput[i] = __json_replacer(pyinput[i], pytokens)
return pyinput
if not isinstance(pyinput, dict):
return pyinput
res = {}
for k, v in pyinput.items():
if isinstance(v, str):
v = __replace(v, pytokens)
if isinstance(v, list):
for i in range(len(v)):
v[i] = __json_replacer(v[i], pytokens)
if isinstance(v, dict):
v = __json_replacer(v, pytokens)
res[k] = v
return res
Примечание: проект с открытым исходным кодом! Если вы предоставите решение, не стесняйтесь открывать PR и брать кредиты :) Ссылка на проект GitHub
Вы можете сократить
if not isinstance(pyinput, dict):
return pyinput
res = {}
for k, v in pyinput.items():
if isinstance(v, str):
v = __replace(v, pytokens)
if isinstance(v, list):
for i in range(len(v)):
v[i] = __json_replacer(v[i], pytokens)
if isinstance(v, dict):
v = __json_replacer(v, pytokens)
res[k] = v
к
if isinstance(pyinput, dict):
return {k: __json_replacer(v, pytokens) for k, v in pyinput.items()}
Затем это делегирует проверку списка приведенному выше коду, поэтому внесите небольшое изменение в обработчик списка. Изменить это
if isinstance(pyinput, list):
for i in range(len(pyinput)):
pyinput[i] = __json_replacer(pyinput[i], pytokens)
return pyinput
к
if isinstance(pyinput, list):
return [__json_replacer(v, pytokens) for v in pyinput]
Собираем это вместе
def __json_replacer(pyinput, pytokens):
if isinstance(pyinput, str):
return __replace(pyinput, pytokens)
if isinstance(pyinput, list):
return [__json_replacer(v, pytokens) for v in pyinput]
if isinstance(pyinput, dict):
return {k: __json_replacer(v, pytokens) for k, v in pyinput.items()}
return pyinput
Бег
>>> __json_replacer({"a": {"b": "x{{ppp}}z"}}, {"ppp": "y"})
# {'a': {'b': 'xyz'}}
Как и ожидалось.
Я бы не советовал рассматривать весь объект как строку и выполнять замену строки, поскольку в конечном итоге вы можете выполнять операции замены и ключей, что может иметь непредвиденные последствия (например, дублирование или повреждение ключей).
@LorenzoMogica, спасибо, сделал пиар github.com/lorenzua02/json-replacer/pull/1
Вам не нужно рассматривать весь ввод как dict
. Вы можете прочитать весь файл как string
и просто использовать функцию __replace()
.
почти похоже, что json (как строка) может быть шаблоном jinja.
Это может привести к непреднамеренному изменению ключей, если они имеют «ppp» или любой другой токен, что приведет к непредвиденным последствиям (подумайте о дублированных ключах и т. д.). Это следует упомянуть в ответе как потенциальное предостережение, поскольку оно не идеально.
Это намного чище (и все мои тесты проходят!): именно то, что я искал. Не стесняйтесь брать кредиты, открывая PR (ссылка на проект выше в вопросе), спасибо! P.S.: Да, я согласен: рассматривать весь объект как строку не вариант