def json(
        self,
        data_columns: Optional[List] = None,
        num_rows: int = 10,
        indent: Optional[int] = None,
        cls: Optional[Type[JSONEncoder]] = None,
    ) -> str:
        """
        Generate random JSON structure values.

        Using a dictionary or list of records that is passed as ``data_columns``,
        define the structure that is used to build JSON structures.  For complex
        data structures it is recommended to use the dictionary format.

        Data Column Dictionary format:
            {'key name': 'definition'}

        The definition can be 'provider', 'provider:argument_group', tokenized
        'string {{ provider:argument_group }}' that is passed to the python
        provider method pystr_format() for generation, or a fixed '@word'.
        Using Lists, Tuples, and Dicts as a definition for structure.

        Example:
            fake.set_arguments('top_half', {'min_value': 50, 'max_value': 100})
            fake.json(data_columns={'Name': 'name', 'Score': 'pyint:top_half'})

        Data Column List format:
            [('key name', 'definition', {'arguments'})]

        With the list format the definition can be a list of records, to create
        a list within the structure data.  For literal entries within the list,
        set the 'field_name' to None.

        :param data_columns: specification for the data structure
        :type data_columns: dict
        :param num_rows: number of rows the returned
        :type num_rows: int
        :param indent: number of spaces to indent the fields
        :type indent: int
        :param cls: optional json encoder to use for non-standard objects such as datetimes
        :type cls: json.JSONEncoder
        :return: Serialized JSON data
        :rtype: str

        :sample: data_columns={'Spec': '@1.0.1', 'ID': 'pyint',
                'Details': {'Name': 'name', 'Address': 'address'}}, num_rows=2
        :sample: data_columns={'Candidates': ['name', 'name', 'name']},
                num_rows=1
        :sample: data_columns=[('Name', 'name'), ('Points', 'pyint',
                {'min_value': 50, 'max_value': 100})], num_rows=1
        """
        default_data_columns = {
            "name": "{{name}}",
            "residency": "{{address}}",
        }
        data_columns: Union[List, Dict] = data_columns if data_columns else default_data_columns

        def process_list_structure(data: Sequence[Any]) -> Any:
            entry: Dict[str, Any] = {}

            for name, definition, *arguments in data:
                kwargs = arguments[0] if arguments else {}

                if not isinstance(kwargs, dict):
                    raise TypeError("Invalid arguments type. Must be a dictionary")

                if name is None:
                    return self._value_format_selection(definition, **kwargs)

                if isinstance(definition, tuple):
                    entry[name] = process_list_structure(definition)
                elif isinstance(definition, (list, set)):
                    entry[name] = [process_list_structure([item]) for item in definition]
                else:
                    entry[name] = self._value_format_selection(definition, **kwargs)
            return entry

        def process_dict_structure(data: Union[int, float, bool, Dict[str, Any]]) -> Any:
            entry: Dict[str, Any] = {}

            if isinstance(data, str):
                return self._value_format_selection(data)

            if isinstance(data, dict):
                for name, definition in data.items():
                    if isinstance(definition, (tuple, list, set)):
                        entry[name] = [process_dict_structure(item) for item in definition]
                    elif isinstance(definition, (dict, int, float, bool)):
                        entry[name] = process_dict_structure(definition)
                    else:
                        entry[name] = self._value_format_selection(definition)
                return entry

            return data

        def create_json_structure(data_columns: Union[Dict, List]) -> dict:
            if isinstance(data_columns, dict):
                return process_dict_structure(data_columns)

            if isinstance(data_columns, list):
                return process_list_structure(data_columns)

            raise TypeError("Invalid data_columns type. Must be a dictionary or list")

        if num_rows == 1:
            return json.dumps(create_json_structure(data_columns), indent=indent, cls=cls)

        data = [create_json_structure(data_columns) for _ in range(num_rows)]
        return json.dumps(data, indent=indent, cls=cls)

        Generate random JSON structure values.

        Using a dictionary or list of records that is passed as ``data_columns``,
        define the structure that is used to build JSON structures.  For complex
        data structures it is recommended to use the dictionary format.

        Data Column Dictionary format:
            {'key name': 'definition'}

        The definition can be 'provider', 'provider:argument_group', tokenized
        'string {{ provider:argument_group }}' that is passed to the python
        provider method pystr_format() for generation, or a fixed '@word'.
        Using Lists, Tuples, and Dicts as a definition for structure.

        Example:
            fake.set_arguments('top_half', {'min_value': 50, 'max_value': 100})
            fake.json(data_columns={'Name': 'name', 'Score': 'pyint:top_half'})

        Data Column List format:
            [('key name', 'definition', {'arguments'})]

        With the list format the definition can be a list of records, to create
        a list within the structure data.  For literal entries within the list,
        set the 'field_name' to None.

        :param data_columns: specification for the data structure
        :type data_columns: dict
        :param num_rows: number of rows the returned
        :type num_rows: int
        :param indent: number of spaces to indent the fields
        :type indent: int
        :param cls: optional json encoder to use for non-standard objects such as datetimes
        :type cls: json.JSONEncoder
        :return: Serialized JSON data
        :rtype: str

        :sample: data_columns={'Spec': '@1.0.1', 'ID': 'pyint',
                'Details': {'Name': 'name', 'Address': 'address'}}, num_rows=2
        :sample: data_columns={'Candidates': ['name', 'name', 'name']},
                num_rows=1
        :sample: data_columns=[('Name', 'name'), ('Points', 'pyint',
                {'min_value': 50, 'max_value': 100})], num_rows=1
        
faker/providers/misc/__init__.py