Skip to content

# JsonContainerComponent

Модуль для работы с JSON-контейнерами на веб-страницах.

Содержит компонент для чтения и проверки JSON-данных в контейнерах. Использует playwright для взаимодействия с элементами страницы.

## `JsonContainerComponent`

Bases: `BaseComponent`

Компонент для работы с JSON-данными на странице.

Предоставляет методы чтения и проверки JSON-данных в контейнерах.

Source code in `components\json_container_component.py`

     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    class JsonContainerComponent(BaseComponent):
        """Компонент для работы с JSON-данными на странице.

        Предоставляет методы чтения и проверки JSON-данных в контейнерах.
        """

        def __init__(self, page: Page) -> None:
            """Инициализирует JSON-контейнер.

            Args:
                page: Экземпляр страницы Playwright.
            """
            self.page = page

        def format_json_string(self, json_string: str) -> str:
            """Форматирует строку JSON для корректного парсинга.

            Args:
                json_string: Сырая строка с JSON-данными.

            Returns:
                str: Отформатированная строка JSON.
            """
            lines = json_string.splitlines()
            formatted_lines = []
            stack = []  # Стек для отслеживания вложенности
            current_indent = 0

            for line in lines:
                line = line.strip()
                if not line:
                    continue

                # Определяем тип текущей строки
                if line in ['{', '[']:
                    formatted_lines.append('  ' * current_indent + line)
                    stack.append(line)
                    current_indent += 1
                elif line in ['}', ']']:
                    current_indent -= 1
                    if stack and stack[-1] in ['{', '[']:
                        stack.pop()
                    formatted_lines.append('  ' * current_indent + line)
                elif re.match(r'^\d+:\{', line):
                    formatted_lines.append('  ' * current_indent + '{')
                    stack.append('{')
                    current_indent += 1
                elif ':' in line:
                    key, value = line.split(':', 1)
                    key = key.strip()
                    value = value.strip()

                    if not (key.startswith('"') and key.endswith('"')):
                        key = f'"{key}"'

                    if value in ['{', '[']:
                        formatted_line = f'{key}: {value}'
                        formatted_lines.append('  ' * current_indent + formatted_line)
                        stack.append(value)
                        current_indent += 1
                    elif value in ['}', ']']:
                        current_indent -= 1
                        formatted_line = f'{key}: {value}'
                        formatted_lines.append('  ' * current_indent + formatted_line)
                        if stack:
                            stack.pop()
                    else:
                        if (value and not value.isdigit() and
                            not value.replace('.', '', 1).isdigit() and
                            value not in ['true', 'false', 'null'] and
                            not value.startswith('"') and not value.endswith('"') and
                            not value.startswith('{') and not value.startswith('[')):
                            value = f'"{value}"'

                        formatted_line = f'{key}: {value}'
                        formatted_lines.append('  ' * current_indent + formatted_line)
                else:
                    formatted_lines.append('  ' * current_indent + line)

            # Добавляем запятые где необходимо
            result = []
            total_lines = len(formatted_lines)

            for i, current_line in enumerate(formatted_lines):
                if i < total_lines - 1:
                    next_line = formatted_lines[i + 1]
                    in_array = any(bracket == '[' for bracket in stack)

                    # Упрощенная проверка условий для запятой
                    no_comma_condition1 = current_line.endswith(('{', '[', ','))
                    no_comma_condition2 = next_line.strip().endswith(('}', ']'))
                    no_comma_condition3 = next_line.strip().startswith(('}', ']'))
                    no_comma_condition4 = in_array and next_line.strip() == ']'

                    should_add_comma = not (no_comma_condition1 or no_comma_condition2 or
                                          no_comma_condition3 or no_comma_condition4)

                    # Специальный случай для элементов массива
                    if (in_array and current_line.strip() == '}' and
                        next_line.strip() != ']' and not next_line.strip().startswith('}')):
                        should_add_comma = True

                    if should_add_comma:
                        current_line += ','

                result.append(current_line)

            return '\n'.join(result)

        def read_data(self, locator: Any) -> Dict:
            """Читает и форматирует JSON-данные из указанного локатора.

            Args:
                locator: Локатор элемента с JSON-данными.

            Returns:
                dict: Распарсенный JSON-объект.

            Raises:
                json.JSONDecodeError: Если данные не могут быть преобразованы в JSON.
            """
            loc = self.get_locator(locator)
            json_string = loc.inner_text()
            formatted_json_string = self.format_json_string(json_string)

            try:
                data = json.loads(formatted_json_string)
            except json.JSONDecodeError as e:
                logger.error("JSON decode error: %s", e)
                logger.error("Formatted JSON: %s", formatted_json_string)
                assert False, f"Invalid json content. Error: {e}"

            return data

        def check_json_equals(self, actual: Any, expected: Any, msg: str) -> None:
            """Сравнивает JSON-объекты на идентичность.

            Args:
                actual: Фактический JSON-объект.
                expected: Ожидаемый JSON-объект.
                msg: Сообщение об ошибке.

            Raises:
                AssertionError: Если объекты не идентичны.
            """
            diff = jsondiff.diff(expected, actual, syntax='symmetric')
            assert len(diff) == 0, f"{msg}. DIFF is {diff}"

### `__init__(page)`

Инициализирует JSON-контейнер.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`page` | `Page` | Экземпляр страницы Playwright. | required

Source code in `components\json_container_component.py`

    24
    25
    26
    27
    28
    29
    30
    def __init__(self, page: Page) -> None:
        """Инициализирует JSON-контейнер.

        Args:
            page: Экземпляр страницы Playwright.
        """
        self.page = page

### `check_json_equals(actual, expected, msg)`

Сравнивает JSON-объекты на идентичность.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`actual` | `Any` | Фактический JSON-объект. | required
`expected` | `Any` | Ожидаемый JSON-объект. | required
`msg` | `str` | Сообщение об ошибке. | required

Raises:

Type | Description
--- | ---
`AssertionError` | Если объекты не идентичны.

Source code in `components\json_container_component.py`

    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    def check_json_equals(self, actual: Any, expected: Any, msg: str) -> None:
        """Сравнивает JSON-объекты на идентичность.

        Args:
            actual: Фактический JSON-объект.
            expected: Ожидаемый JSON-объект.
            msg: Сообщение об ошибке.

        Raises:
            AssertionError: Если объекты не идентичны.
        """
        diff = jsondiff.diff(expected, actual, syntax='symmetric')
        assert len(diff) == 0, f"{msg}. DIFF is {diff}"

### `format_json_string(json_string)`

Форматирует строку JSON для корректного парсинга.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`json_string` | `str` | Сырая строка с JSON-данными. | required

Returns:

Name | Type | Description
--- | --- | ---
`str` | `str` | Отформатированная строка JSON.

Source code in `components\json_container_component.py`

     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    def format_json_string(self, json_string: str) -> str:
        """Форматирует строку JSON для корректного парсинга.

        Args:
            json_string: Сырая строка с JSON-данными.

        Returns:
            str: Отформатированная строка JSON.
        """
        lines = json_string.splitlines()
        formatted_lines = []
        stack = []  # Стек для отслеживания вложенности
        current_indent = 0

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # Определяем тип текущей строки
            if line in ['{', '[']:
                formatted_lines.append('  ' * current_indent + line)
                stack.append(line)
                current_indent += 1
            elif line in ['}', ']']:
                current_indent -= 1
                if stack and stack[-1] in ['{', '[']:
                    stack.pop()
                formatted_lines.append('  ' * current_indent + line)
            elif re.match(r'^\d+:\{', line):
                formatted_lines.append('  ' * current_indent + '{')
                stack.append('{')
                current_indent += 1
            elif ':' in line:
                key, value = line.split(':', 1)
                key = key.strip()
                value = value.strip()

                if not (key.startswith('"') and key.endswith('"')):
                    key = f'"{key}"'

                if value in ['{', '[']:
                    formatted_line = f'{key}: {value}'
                    formatted_lines.append('  ' * current_indent + formatted_line)
                    stack.append(value)
                    current_indent += 1
                elif value in ['}', ']']:
                    current_indent -= 1
                    formatted_line = f'{key}: {value}'
                    formatted_lines.append('  ' * current_indent + formatted_line)
                    if stack:
                        stack.pop()
                else:
                    if (value and not value.isdigit() and
                        not value.replace('.', '', 1).isdigit() and
                        value not in ['true', 'false', 'null'] and
                        not value.startswith('"') and not value.endswith('"') and
                        not value.startswith('{') and not value.startswith('[')):
                        value = f'"{value}"'

                    formatted_line = f'{key}: {value}'
                    formatted_lines.append('  ' * current_indent + formatted_line)
            else:
                formatted_lines.append('  ' * current_indent + line)

        # Добавляем запятые где необходимо
        result = []
        total_lines = len(formatted_lines)

        for i, current_line in enumerate(formatted_lines):
            if i < total_lines - 1:
                next_line = formatted_lines[i + 1]
                in_array = any(bracket == '[' for bracket in stack)

                # Упрощенная проверка условий для запятой
                no_comma_condition1 = current_line.endswith(('{', '[', ','))
                no_comma_condition2 = next_line.strip().endswith(('}', ']'))
                no_comma_condition3 = next_line.strip().startswith(('}', ']'))
                no_comma_condition4 = in_array and next_line.strip() == ']'

                should_add_comma = not (no_comma_condition1 or no_comma_condition2 or
                                      no_comma_condition3 or no_comma_condition4)

                # Специальный случай для элементов массива
                if (in_array and current_line.strip() == '}' and
                    next_line.strip() != ']' and not next_line.strip().startswith('}')):
                    should_add_comma = True

                if should_add_comma:
                    current_line += ','

            result.append(current_line)

        return '\n'.join(result)

### `read_data(locator)`

Читает и форматирует JSON-данные из указанного локатора.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`locator` | `Any` | Локатор элемента с JSON-данными. | required

Returns:

Name | Type | Description
--- | --- | ---
`dict` | `Dict` | Распарсенный JSON-объект.

Raises:

Type | Description
--- | ---
`JSONDecodeError` | Если данные не могут быть преобразованы в JSON.

Source code in `components\json_container_component.py`

    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    def read_data(self, locator: Any) -> Dict:
        """Читает и форматирует JSON-данные из указанного локатора.

        Args:
            locator: Локатор элемента с JSON-данными.

        Returns:
            dict: Распарсенный JSON-объект.

        Raises:
            json.JSONDecodeError: Если данные не могут быть преобразованы в JSON.
        """
        loc = self.get_locator(locator)
        json_string = loc.inner_text()
        formatted_json_string = self.format_json_string(json_string)

        try:
            data = json.loads(formatted_json_string)
        except json.JSONDecodeError as e:
            logger.error("JSON decode error: %s", e)
            logger.error("Formatted JSON: %s", formatted_json_string)
            assert False, f"Invalid json content. Error: {e}"

        return data