Выдать встроенный объект Python как собственный JSON в YAML - PullRequest
0 голосов
/ 30 мая 2018

Я импортирую тесты веб-сервиса из Excel и сериализую их как YAML.

Но, пользуясь тем, что YAML является надмножеством JSON, я бы хотел, чтобы часть запроса в тесте была действительной JSON, т.е.иметь разделители, кавычки и запятые.

Это позволит нам вырезать и вставлять запросы между автоматическим набором тестов и инструментами ручного тестирования (например, Почтальон).

Так вот, как бы я хотелtest to look (упрощенный):

- properties:
    METHOD: GET
    TYPE: ADDRESS
    Request URL: /addresses
    testCaseId: TC2
  request:
    {
        "unitTypeCode": "",
        "unitNumber": "15",
        "levelTypeCode": "L",
        "roadNumber1": "810",
        "roadName": "HAY",
        "roadTypeCode": "ST",
        "localityName": "PERTH",
        "postcode": "6000",
        "stateTerritoryCode": "WA"
    }

В Python мой объект запроса имеет атрибут dict с именем fields, который является частью объекта, который будет сериализован как JSON.Вот что я попробовал:

import yaml

def request_presenter(dumper, request):
    json_string = json.dumps(request.fields, indent=8)
    return dumper.represent_str(json_string)

yaml.add_representer(Request, request_presenter)

test = Test(...including embedded request object)
serialised_test = yaml.dump(test)

Я получаю:

- properties:
    METHOD: GET
    TYPE: ADDRESS
    Request URL: /addresses
    testCaseId: TC2
  request: "{
    \"unitTypeCode\": \"\",\n
    \"unitNumber\": \"15\",\n
    \"levelTypeCode": \"L\",\n
    \"roadNumber1\": \"810\",\n
    \"roadName\": \"HAY\",\n
    \"roadTypeCode\": \"ST\",\n
    \"localityName\": \"PERTH\",\n
    \"postcode\": \"6000\",\n
    \"stateTerritoryCode\": \"WA\"\n
  }"

... только хуже, потому что все в одной строке и везде с пробелами.

Я пытался использовать стиль | для буквенных многострочных строк, который помогает с переносами строк и экранированными кавычками (это более сложный вопрос, но этот ответ был полезен.) Однако экранированный или многострочныйрезультат по-прежнему является строкой, которую необходимо анализировать отдельно.

Как я могу остановить PyYaml, анализирующий блок JSON как строку, и заставить его просто принять блок текста как часть выданного YAML?Я предполагаю, что это как-то связано с переопределением эмиттера, но я мог бы помочь.Если возможно, я бы хотел избежать пост-обработки сериализованного теста для достижения этой цели.

1 Ответ

0 голосов
/ 07 июня 2019

Хорошо, так что это было решение, которое я придумал.Создайте YAML с меткой заранее.Метка метки отмечает место, куда должен быть вставлен JSON, а также определяет отступ корневого уровня блока JSON.

import os
import itertools
import json


def insert_json_in_yaml(pre_insert_yaml, key, obj_to_serialise):
    marker = '%s: null' % key
    marker_line = line_of_first_occurrence(pre_insert_yaml, marker)
    marker_indent = string_indent(marker_line)
    serialised = json.dumps(obj_to_serialise, indent=marker_indent + 4)
    key_with_json = '%s: %s' % (key, serialised)
    serialised_with_json = pre_insert_yaml.replace(marker, key_with_json)
    return serialised_with_json


def line_of_first_occurrence(basestring, substring):
    """
    return line number of first occurrence of substring
    """
    lineno = lineno_of_first_occurrence(basestring, substring)
    return basestring.split(os.linesep)[lineno]


def string_indent(s):
    """
    return indentation of a string (no of spaces before a nonspace)
    """
    spaces = ''.join(itertools.takewhile(lambda c: c == ' ', s))
    return len(spaces)


def lineno_of_first_occurrence(basestring, substring):
    """
    return line number of first occurrence of substring
    """
    return basestring[:basestring.index(substring)].count(os.linesep)


embedded_object = {
    "unitTypeCode": "",
    "unitNumber": "15",
    "levelTypeCode": "L",
    "roadNumber1": "810",
    "roadName": "HAY",
    "roadTypeCode": "ST",
    "localityName": "PERTH",
    "postcode": "6000",
    "stateTerritoryCode": "WA"
}
yaml_string = """
---

- properties:
    METHOD: GET
    TYPE: ADDRESS
    Request URL: /addresses
    testCaseId: TC2
  request: null
  after_request: another value
"""

>>> print(insert_json_in_yaml(yaml_string, 'request', embedded_object))
- properties:
    METHOD: GET
    TYPE: ADDRESS
    Request URL: /addresses
    testCaseId: TC2
  request: {
    "unitTypeCode": "",
    "unitNumber": "15",
    "levelTypeCode": "L",
    "roadNumber1": "810",
    "roadName": "HAY",
    "roadTypeCode": "ST",
    "localityName": "PERTH",
    "postcode": "6000",
    "stateTerritoryCode": "WA"
  }
  after_request: another value

...