public function serialize(
            $value,
            XmlStreamWriter $xmlWriter,
            string $tagName = null,
            string $elementTagName = null
    ): XmlStreamWriter {
        switch (gettype($value)) {
            case 'NULL':
                $this->serializeNull($xmlWriter, $tagName);
                break;

            case 'boolean':
                $this->serializeBool($value, $xmlWriter, $tagName);
                break;

            case 'string':
            case 'integer':
            case 'double':
                $this->serializeScalarValue($value, $xmlWriter, $tagName);
                break;

            case 'array':
                $this->serializeArray($value, $xmlWriter, $tagName, $elementTagName);
                break;

            case 'object':
                if ($value instanceof \Traversable && !annotationsOf($value)->contain('XmlNonTraversable')) {
                    if (null === $tagName && $value instanceof \Traversable && annotationsOf($value)->contain('XmlTag')) {
                        $annotation = annotationsOf($value)->firstNamed('XmlTag');
                        $tagName = $annotation->getTagName();
                        $elementTagName = $annotation->getElementTagName();
                    }

                    $this->serializeArray($value, $xmlWriter, $tagName, $elementTagName);
                } else {
                    $this->serializeObject($value, $xmlWriter, $tagName);
                }
                break;

            default:
                // nothing to do
        }

        return $xmlWriter;
    }