Получить список полей из списка объектов (dict), импортированных из json

Из этого json:

{
  "obj": [
    {
      "int": 0
    },
    {
      "int": 1
    }
  ]
}

Я хотел бы получить список свойств int поля obj, чтобы я мог выполнить агрегацию в списке, например. взяв среднее значение int в obj, используя синтаксис Jinja2.

Я пытался использовать фильтр selectattr("int") в сочетании с фильтром sum, но получаю `неподдерживаемые типы операндов для +: 'int' и 'dict'.

Я использую Jinja2 с docxtpl для создания файла docx из шаблона.

import json
j = '{"obj": [{"int": 0},{"int": 1}]}'
context = json.loads(j)

from docxtpl import DocxTemplate
import jinja2

tpl = DocXtemplate('template.docx')
tpl.render(context, jinja_env)
tpl.save('out.docx')

Шаблон docx содержит одну строку:

{{obj|selectattr("int")|sum}}

Я хотел бы видеть 1 в файле doxc при получении суммы, как я пытался сделать чуть выше, я также хотел бы иметь возможность делать среднее значение и в конечном итоге повторно использовать его в других операциях.

EDIT: В качестве ответа тем, кто говорит, что я должен сделать это внутри python:

Я разрабатываю инструмент для создания отчетов docx из базы данных и файла шаблона. Первая часть инструмента — это программа на C++, которая генерирует данные json из базы данных. Вторая часть — это скрипт для генерации отчета в формате docx из шаблона с использованием Jinja2. Я бы предпочел, чтобы пользователь возился только с шаблоном и не модифицировал скрипт, также я хотел бы иметь некоторую модульность и иметь возможность вычислять агрегации по дереву json. Поскольку Jinja2 предоставляет sum для списка и другие виды операций, я подумал, что это может добавить еще один уровень модульности без необходимости писать дополнительный код. На данный момент я уже добавил для пользователя возможность выполнять агрегирование в дереве json и вставлять его в дерево узлов, но мое решение не идеально, и оно достигается за счет предоставления другого файла шаблона для генерации данных, что делает инструмент более сложный.

Надеюсь, теперь понятно, почему я не хочу делать это в сценарии.

Edit2: немного более сложный пример:

{
    "arr": [
        {
            "obj": [
                {
                    "name": 0
                },
                {
                    "name": 1
                }
            ]
        },
        {
            "obj": [
                {
                    "name": 0
                },
                {
                    "name": 1
                }
            ]
        }
    ]
}

Я хотел бы иметь возможность сделать что-то вроде:

{{arr|get_attr_list('obj')|get_attr_list('int')}}

Еще лучшим решением было бы расширение списка в Jinja2 напрямую:

{{ [ y[int] for y in x[obj] for x in arr ] | sum }}

person Nick Skywalker    schedule 09.07.2019    source источник
comment
См. здесь stackoverflow.com/questions/41793024/, но вам лучше сделать это с помощью python   -  person balderman    schedule 09.07.2019
comment
Я ищу что-то менее громоздкое. Смотрите мой ответ на этот пост, чтобы иметь представление.   -  person Nick Skywalker    schedule 09.07.2019


Ответы (3)


Это абсолютно не то, что вы должны делать в Джиндже. Это язык шаблонов для отображения вещей.

Вы должны сделать это в Python, где это простой однострочный код:

sum(item["int"] for item in context["obj"])
person Daniel Roseman    schedule 09.07.2019
comment
Спасибо, я отредактировал свой пост, чтобы объяснить, почему я не хочу делать это на питоне. - person Nick Skywalker; 09.07.2019

Здесь

d= {
  "obj": [
    {
      "int": 0
    },
    {
      "int": 1
    }
  ]
}
# create a list
lst = [e['int'] for e in d['obj']]
print(lst)
# now you can do stats on this list
import statistics
mean = statistics.mean(lst)
print(mean)
person balderman    schedule 09.07.2019
comment
Спасибо, я отредактировал свой пост, чтобы объяснить, почему я не хочу делать это на питоне. - person Nick Skywalker; 09.07.2019
comment
Я добавил комментарий с примером выполнения sum в шаблоне - person balderman; 09.07.2019

Полный ответ работает как на простом, так и на более сложном примере:

Я могу предоставить фильтр, который возвращает список, чтобы составить его, я должен проверить, является ли интересующее поле списком, если да, я должен сгладить результат. Пограничный случай должен быть более определенным, но он работает:

def get_list(value, arg):
  if type(value) is dict:
    return value[arg]
  elif type(value) is list:
    if len(value) == 0:
        return []
    else:
      res = [ x[arg] for x in value ]
      if type(res[0]) is list:
        res = list(itertools.chain(*res))
      return res
  else:
    return value

Затем эта работа, как и ожидалось:

первый пример

{{ obj|get_list("int")|sum }}

(выход 1)

второй пример

{{ arr|get_list("obj")|get_list("int")|sum }}

(выход 2)

person Nick Skywalker    schedule 09.07.2019