在YAML文档

时间:2018-05-29 21:01:58

标签: json pyyaml

我正在从Excel导入Web服务测试并将它们序列化为YAML。

但是利用YAML作为JSON的超集,我希望测试的请求部分是有效的JSON,即具有分隔符,引号和逗号。

这将允许我们在自动测试套件和手动测试工具(例如Postman)之间剪切和粘贴请求。

所以这就是我想要一个看起来(简化)的测试:

- 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的{​​{1}}属性,它是要作为JSON序列化的对象的一部分。这就是我试过的:

fields

我得到了:

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 }" 样式用于文字多行字符串,这有助于换行和转义引号(它更复杂,但this answer很有用。)但是,转义或多行,结果仍然是一个需要单独解析的字符串。

如何阻止PyYaml将JSON块分析为字符串并使其只接受一段文本作为发出的YAML的一部分?我猜它与覆盖发射器有关,但我可以使用一些帮助。如果可能的话,我想避免对序列化测试进行后处理以实现这一目标。

1 个答案:

答案 0 :(得分:0)

好的,这就是我想出的解决方案。提前生成带有地标的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