Фон
Для некоторого фона я пытаюсь создать инструмент, который преобразует рабочие листы в вызовы API, используя Python 3.5
Для преобразования ячеек таблицы в схему, необходимую для вызова API, я начал с использования javascript-подобного синтаксиса для заголовков, используемых в электронной таблице. например:
Заголовок рабочего листа (строка)
dict.list[0].id
Словарь Python
{
"dict":
"list": [
{"id": "my cell value"}
]
}
Также возможно, что схема заголовка могла иметь вложенные массивы / dicts:
one.two[0].three[0].four.five[0].six
И мне также нужно добавить объект после того, как он был создан, когда я прохожу каждый заголовок.
Что я пробовал
add_branch
На основе https://stackoverflow.com/a/47276490/2903486 Я могу настроить вложенные словари, используя такие значения, как one.two.three.four
, и я могу добавить к существующему словарю, когда я прохожу строки, но я не смог добавить в поддержку массивов:
def add_branch(tree, vector, value):
key = vector[0]
tree[key] = value \
if len(vector) == 1 \
else add_branch(tree[key] if key in tree else {},
vector[1:],
value)
return tree
file = Worksheet(filePath, sheet).readRow()
rowList = []
for row in file:
rowObj = {}
for colName, rowValue in row.items():
rowObj.update(add_branch(rowObj, colName.split("."), rowValue))
rowList.append(rowObj)
return rowList
Моя собственная версия add_branch
import re, json
def branch(tree, vector, value):
"""
Used to convert JS style notation (e.g dict.another.array[0].id) to a python object
Originally based on https://stackoverflow.com/a/47276490/2903486
"""
# Convert Boolean
if isinstance(value, str):
value = value.strip()
if value.lower() in ['true', 'false']:
value = True if value.lower() == "true" else False
# Convert JSON
try:
value = json.loads(value)
except:
pass
key = vector[0]
arr = re.search('\[([0-9]+)\]', key)
if arr:
arr = arr.group(0)
key = key.replace(arr, '')
arr = arr.replace('[', '').replace(']', '')
newArray = False
if key not in tree:
tree[key] = []
tree[key].append(value \
if len(vector) == 1 \
else branch({} if key in tree else {},
vector[1:],
value))
else:
isInArray = False
for x in tree[key]:
if x.get(vector[1:][0], False):
isInArray = x[vector[1:][0]]
if isInArray:
tree[key].append(value \
if len(vector) == 1 \
else branch({} if key in tree else {},
vector[1:],
value))
else:
tree[key].append(value \
if len(vector) == 1 \
else branch({} if key in tree else {},
vector[1:],
value))
if len(vector) == 1 and len(tree[key]) == 1:
tree[key] = value.split(",")
else:
tree[key] = value \
if len(vector) == 1 \
else branch(tree[key] if key in tree else {},
vector[1:],
value)
return tree
Что еще нужно помочь
Мое решение для веток работает довольно хорошо сейчас, после добавления некоторых вещей, но мне интересно, если я делаю что-то не так / грязно, или есть лучший способ справиться с тем, где я редактирую вложенные массивы (моя попытка началась в разделе if IsInArray
кода)
Я ожидаю, что эти два заголовка отредактируют последний массив, но вместо этого я создаю дубликат словаря для первого массива:
file = [{
"one.array[0].dict.arrOne[0]": "1,2,3",
"one.array[0].dict.arrTwo[0]": "4,5,6"
}]
rowList = []
for row in file:
rowObj = {}
for colName, rowValue in row.items():
rowObj.update(add_branch(rowObj, colName.split("."), rowValue))
rowList.append(rowObj)
return rowList
Выходы:
[
{
"one": {
"array": [
{
"dict": {
"arrOne": [
"1",
"2",
"3"
]
}
},
{
"dict": {
"arrTwo": [
"4",
"5",
"6"
]
}
}
]
}
}
]
Вместо:
[
{
"one": {
"array": [
{
"dict": {
"arrOne": [
"1",
"2",
"3"
],
"arrTwo": [
"4",
"5",
"6"
]
}
}
]
}
}
]