# Source code for xsdata.formats.dataclass.serializers.json

    import json
    import warnings
    from dataclasses import dataclass
    from dataclasses import field
    from enum import Enum
    from io import StringIO
    from typing import Any
    from typing import Callable
    from typing import Dict
    from typing import Iterator
    from typing import Optional
    from typing import TextIO
    from typing import Tuple

    from xsdata.formats.bindings import AbstractSerializer
    from xsdata.formats.converter import converter
    from xsdata.formats.dataclass.context import XmlContext
    from xsdata.formats.dataclass.models.elements import XmlVar
    from xsdata.formats.dataclass.serializers.config import SerializerConfig
    from xsdata.utils import collections

    def filter_none(x: Tuple) -> Dict:
        return {k: v for k, v in x if v is not None}

    class DictFactory:
        """Dictionary factory types."""

        FILTER_NONE = filter_none

    @dataclass
    class JsonSerializer(AbstractSerializer):
        """
        Json serializer for dataclasses.

        :param config: Serializer configuration
        :param context: Model context provider
        :param dict_factory: Override default dict factory to add further logic
        :param dump_factory: Override default json.dump call with another implementation
        :param indent: Output indentation level
        """

        config: SerializerConfig = field(default_factory=SerializerConfig)
        context: XmlContext = field(default_factory=XmlContext)
        dict_factory: Callable = field(default=dict)
        dump_factory: Callable = field(default=json.dump)
        indent: Optional[int] = field(default=None)

        def render(self, obj: object) -> str:
            """Convert the given object tree to json string."""
            output = StringIO()
            self.write(output, obj)
            return output.getvalue()

        def write(self, out: TextIO, obj: Any):
            """
            Write the given object tree to the output text stream.

            :param out: The output stream
            :param obj: The input dataclass instance
            """

            indent: Optional[int] = None
            if self.indent:
                warnings.warn(
                    "JsonSerializer indent property is deprecated, use SerializerConfig",
                    DeprecationWarning,
                )
                indent = self.indent
            elif self.config.pretty_print:
                indent = 2

            self.dump_factory(self.convert(obj), out, indent=indent)

        def convert(self, obj: Any, var: Optional[XmlVar] = None) -> Any:
            if var is None or self.context.class_type.is_model(obj):

                if collections.is_array(obj):
                    return [self.convert(o) for o in obj]

                return self.dict_factory(self.next_value(obj))

            if collections.is_array(obj):
                return type(obj)(self.convert(v, var) for v in obj)

            if isinstance(obj, (dict, int, float, str, bool)):
                return obj

            if isinstance(obj, Enum):
                return self.convert(obj.value, var)

            return converter.serialize(obj, format=var.format)

        def next_value(self, obj: Any) -> Iterator[Tuple[str, Any]]:
            ignore_optionals = self.config.ignore_default_attributes

            for var in self.context.build(obj.__class__).get_all_vars():
                value = getattr(obj, var.name)
                if var.is_attribute and ignore_optionals and var.is_optional(value):
                    continue

                yield var.local_name, self.convert(value, var)