Разбирать имена полей с разделителями и вложенные имена из параметра URL для частичного ответа
В Flask-RESTful
на основе API, я хочу разрешить клиентам частично получать ответ JSON через?fields=...
параметр. В нем перечислены имена полей (ключи объекта JSON), которые будут использоваться для создания частичного представления большего оригинала.
В простейшей форме это может быть список, разделенный запятыми:
GET /v1/foobar?fields=name,id,date
Это может быть сделано с webargs' DelimitedList поля схемы легко, и нет никаких проблем для меня.
Но, чтобы разрешить представление ключей вложенных объектов, список полей с разделителями может включать произвольно вложенные ключи, заключенные в соответствующие круглые скобки:
GET /v1/foobar?fields=name,id,another(name,id),date
{
"name": "",
"id": "",
"another": {
"name": "",
"id": ""
},
"date": ""
}
GET /v1/foobar?fields=id,one(id,two(id,three(id),date)),date
{
"id": "",
"one": {
"id: "",
"two": {
"id": "",
"three": {
"id": ""
},
"date": ""
}
},
"date": ""
}
GET /v1/foobar?fields=just(me)
{
"just": {
"me: ""
}
}
У меня двоякий вопрос:
Есть ли способ сделать это (проверить и десериализовать) с помощью
webargs
а такжеmarshmallow
изначально?Если нет, то как мне это сделать с помощью структуры синтаксического анализа, такой как
pyparsing
? Любой намек на то, как должна выглядеть грамматика BNF, приветствуется.
1 ответ
У Pyparsing есть несколько полезных встроенных модулей, delimitedList
а также nestedExpr
. Вот аннотированный фрагмент, который создает синтаксический анализатор для ваших значений. (Я также включил пример, в котором элементы вашего списка могут быть больше, чем просто буквенные слова):
import pyparsing as pp
# placeholder element that will be used recursively
item = pp.Forward()
# your basic item type - expand as needed to include other characters or types
word = pp.Word(pp.alphas + '_')
list_element = word
# for instance, add support for numeric values
list_element = word | pp.pyparsing_common.number
# retain x(y, z, etc.) groupings using Group
grouped_item = pp.Group(word + pp.nestedExpr(content=pp.delimitedList(item)))
# define content for placeholder; must use '<<=' operator here, not '='
item <<= grouped_item | list_element
# create parser
parser = pp.Suppress("GET /v1/foobar?fields=") + pp.delimitedList(item)
Вы можете протестировать любое выражение pyparsing, используя runTests
:
parser.runTests("""
GET /v1/foobar?fields=name,id,date
GET /v1/foobar?fields=name,id,another(name,id),date
GET /v1/foobar?fields=id,one(id,two(id,three(id),date)),date
GET /v1/foobar?fields=just(me)
GET /v1/foobar?fields=numbers(1,2,3.7,-26e10)
""", fullDump=False)
Дает:
GET /v1/foobar?fields=name,id,date
['name', 'id', 'date']
GET /v1/foobar?fields=name,id,another(name,id),date
['name', 'id', ['another', ['name', 'id']], 'date']
GET /v1/foobar?fields=id,one(id,two(id,three(id),date)),date
['id', ['one', ['id', ['two', ['id', ['three', ['id']], 'date']]]], 'date']
GET /v1/foobar?fields=just(me)
[['just', ['me']]]
GET /v1/foobar?fields=numbers(1,2,3.7,-26e10)
[['numbers', [1, 2, 3.7, -260000000000.0]]]