File: System\Runtime\Serialization\ExtensionDataReader.cs
Web Access
Project: src\src\libraries\System.Private.DataContractSerialization\src\System.Private.DataContractSerialization.csproj (System.Private.DataContractSerialization)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.Serialization.DataContracts;
using System.Xml;
 
namespace System.Runtime.Serialization
{
    // NOTE: XmlReader methods that are not needed have been left un-implemented
 
    internal sealed class ExtensionDataReader : XmlReader
    {
        private enum ExtensionDataNodeType
        {
            None,
            Element,
            EndElement,
            Text,
            Xml,
            ReferencedElement,
            NullElement,
        }
 
        private ElementData?[]? _elements;
        private ElementData? _element;
        private ElementData? _nextElement;
 
        private ReadState _readState = ReadState.Initial;
        private ExtensionDataNodeType _internalNodeType;
        private XmlNodeType _nodeType;
        private int _depth;
        private string? _localName;
        private string? _ns;
        private string? _prefix;
        private string? _value;
        private int _attributeCount;
        private int _attributeIndex;
 
        private readonly Hashtable _cache = new Hashtable();
        private XmlNodeReader? _xmlNodeReader;
        private Queue<IDataNode>? _deserializedDataNodes;
 
        private static readonly object s_prefixLock = new object();
 
 
        private readonly XmlObjectSerializerReadContext _context;
 
        private static readonly Hashtable s_nsToPrefixTable = new Hashtable();
 
        private static readonly Hashtable s_prefixToNsTable = new Hashtable();
 
        static ExtensionDataReader()
        {
            AddPrefix(Globals.XsiPrefix, Globals.SchemaInstanceNamespace);
            AddPrefix(Globals.SerPrefix, Globals.SerializationNamespace);
            AddPrefix(string.Empty, string.Empty);
        }
 
        internal ExtensionDataReader(XmlObjectSerializerReadContext context)
        {
            _attributeIndex = -1;
            _context = context;
        }
 
        internal void SetDeserializedValue(object? obj)
        {
            IDataNode? deserializedDataNode = (_deserializedDataNodes == null || _deserializedDataNodes.Count == 0) ? null : _deserializedDataNodes.Dequeue();
            if (deserializedDataNode != null && !(obj is IDataNode))
            {
                deserializedDataNode.Value = obj;
                deserializedDataNode.IsFinalValue = true;
            }
        }
 
        internal IDataNode? GetCurrentNode()
        {
            IDataNode? retVal = _element!.dataNode;
            Skip();
            return retVal;
        }
 
        internal void SetDataNode(IDataNode dataNode, string? name, string? ns)
        {
            SetNextElement(dataNode, name, ns, null);
            _element = _nextElement;
            _nextElement = null;
            SetElement();
        }
 
        internal void Reset()
        {
            _localName = null;
            _ns = null;
            _prefix = null;
            _value = null;
            _attributeCount = 0;
            _attributeIndex = -1;
            _depth = 0;
            _element = null;
            _nextElement = null;
            _elements = null;
        }
 
        [MemberNotNullWhen(true, nameof(_xmlNodeReader))]
        [MemberNotNullWhen(false, nameof(_element))]
        private bool IsXmlDataNode { get { return (_internalNodeType == ExtensionDataNodeType.Xml); } }
 
        public override XmlNodeType NodeType { get { return IsXmlDataNode ? _xmlNodeReader.NodeType : _nodeType; } }
        public override string LocalName { get { return IsXmlDataNode ? _xmlNodeReader.LocalName : _localName!; } }
        public override string NamespaceURI { get { return IsXmlDataNode ? _xmlNodeReader.NamespaceURI : _ns!; } }
        public override string Prefix { get { return IsXmlDataNode ? _xmlNodeReader.Prefix : _prefix!; } }
        public override string Value { get { return IsXmlDataNode ? _xmlNodeReader.Value : _value!; } }
        public override int Depth { get { return IsXmlDataNode ? _xmlNodeReader.Depth : _depth; } }
        public override int AttributeCount { get { return IsXmlDataNode ? _xmlNodeReader.AttributeCount : _attributeCount; } }
        public override bool EOF { get { return IsXmlDataNode ? _xmlNodeReader.EOF : (_readState == ReadState.EndOfFile); } }
        public override ReadState ReadState { get { return IsXmlDataNode ? _xmlNodeReader.ReadState : _readState; } }
        public override bool IsEmptyElement { get { return IsXmlDataNode ? _xmlNodeReader.IsEmptyElement : false; } }
        public override bool IsDefault { get { return IsXmlDataNode ? _xmlNodeReader.IsDefault : base.IsDefault; } }
        //public override char QuoteChar { get { return IsXmlDataNode ? xmlNodeReader.QuoteChar : base.QuoteChar; } }
        public override XmlSpace XmlSpace { get { return IsXmlDataNode ? _xmlNodeReader.XmlSpace : base.XmlSpace; } }
        public override string XmlLang { get { return IsXmlDataNode ? _xmlNodeReader.XmlLang : base.XmlLang; } }
        public override string this[int i] { get { return IsXmlDataNode ? _xmlNodeReader[i] : GetAttribute(i); } }
        public override string? this[string name] { get { return IsXmlDataNode ? _xmlNodeReader[name] : GetAttribute(name); } }
        public override string? this[string name, string? namespaceURI] { get { return IsXmlDataNode ? _xmlNodeReader[name, namespaceURI] : GetAttribute(name, namespaceURI); } }
 
        public override bool MoveToFirstAttribute()
        {
            if (IsXmlDataNode)
                return _xmlNodeReader.MoveToFirstAttribute();
 
            if (_attributeCount == 0)
                return false;
            MoveToAttribute(0);
            return true;
        }
 
        public override bool MoveToNextAttribute()
        {
            if (IsXmlDataNode)
                return _xmlNodeReader.MoveToNextAttribute();
 
            if (_attributeIndex + 1 >= _attributeCount)
                return false;
            MoveToAttribute(_attributeIndex + 1);
            return true;
        }
 
        public override void MoveToAttribute(int index)
        {
            if (IsXmlDataNode)
                _xmlNodeReader.MoveToAttribute(index);
            else
            {
                if (index < 0 || index >= _attributeCount)
                    throw new XmlException(SR.InvalidXmlDeserializingExtensionData);
 
                _nodeType = XmlNodeType.Attribute;
                AttributeData attribute = _element.attributes![index];
                _localName = attribute.localName;
                _ns = attribute.ns;
                _prefix = attribute.prefix;
                _value = attribute.value;
                _attributeIndex = index;
            }
        }
 
        public override string? GetAttribute(string name, string? namespaceURI)
        {
            if (IsXmlDataNode)
                return _xmlNodeReader.GetAttribute(name, namespaceURI);
 
            for (int i = 0; i < _element.attributeCount; i++)
            {
                AttributeData attribute = _element.attributes![i];
                if (attribute.localName == name && attribute.ns == namespaceURI)
                    return attribute.value;
            }
 
            return null;
        }
 
        public override bool MoveToAttribute(string name, string? namespaceURI)
        {
            if (IsXmlDataNode)
                return _xmlNodeReader.MoveToAttribute(name, _ns);
 
            for (int i = 0; i < _element.attributeCount; i++)
            {
                AttributeData attribute = _element.attributes![i];
                if (attribute.localName == name && attribute.ns == namespaceURI)
                {
                    MoveToAttribute(i);
                    return true;
                }
            }
 
            return false;
        }
 
        public override bool MoveToElement()
        {
            if (IsXmlDataNode)
                return _xmlNodeReader.MoveToElement();
 
            if (_nodeType != XmlNodeType.Attribute)
                return false;
 
            SetElement();
            return true;
        }
 
        private void SetElement()
        {
            _nodeType = XmlNodeType.Element;
            _localName = _element!.localName;
            _ns = _element.ns;
            _prefix = _element.prefix;
            _value = string.Empty;
            _attributeCount = _element.attributeCount;
            _attributeIndex = -1;
        }
 
        public override string? LookupNamespace(string prefix)
        {
            if (IsXmlDataNode)
                return _xmlNodeReader.LookupNamespace(prefix);
 
            return (string?)s_prefixToNsTable[prefix];
        }
 
        public override void Skip()
        {
            if (IsXmlDataNode)
                _xmlNodeReader.Skip();
            else
            {
                if (ReadState != ReadState.Interactive)
                    return;
                MoveToElement();
                if (IsElementNode(_internalNodeType))
                {
                    int depth = 1;
                    while (depth != 0)
                    {
                        if (!Read())
                            throw new XmlException(SR.InvalidXmlDeserializingExtensionData);
 
                        if (IsElementNode(_internalNodeType))
                            depth++;
                        else if (_internalNodeType == ExtensionDataNodeType.EndElement)
                        {
                            ReadEndElement();
                            depth--;
                        }
                    }
                }
                else
                    Read();
            }
        }
 
        private static bool IsElementNode(ExtensionDataNodeType nodeType)
        {
            return (nodeType == ExtensionDataNodeType.Element ||
                nodeType == ExtensionDataNodeType.ReferencedElement ||
                nodeType == ExtensionDataNodeType.NullElement);
        }
 
        protected override void Dispose(bool disposing)
        {
            if (IsXmlDataNode)
                _xmlNodeReader.Dispose();
            else
            {
                Reset();
                _readState = ReadState.Closed;
            }
 
            base.Dispose(disposing);
        }
 
        public override bool Read()
        {
            if (_nodeType == XmlNodeType.Attribute && MoveToNextAttribute())
                return true;
 
            MoveNext(_element!.dataNode);
 
            switch (_internalNodeType)
            {
                case ExtensionDataNodeType.Element:
                case ExtensionDataNodeType.ReferencedElement:
                case ExtensionDataNodeType.NullElement:
                    PushElement();
                    SetElement();
                    break;
 
                case ExtensionDataNodeType.Text:
                    _nodeType = XmlNodeType.Text;
                    _prefix = string.Empty;
                    _ns = string.Empty;
                    _localName = string.Empty;
                    _attributeCount = 0;
                    _attributeIndex = -1;
                    break;
 
                case ExtensionDataNodeType.EndElement:
                    _nodeType = XmlNodeType.EndElement;
                    _prefix = string.Empty;
                    _ns = string.Empty;
                    _localName = string.Empty;
                    _value = string.Empty;
                    _attributeCount = 0;
                    _attributeIndex = -1;
                    PopElement();
                    break;
 
                case ExtensionDataNodeType.None:
                    if (_depth != 0)
                        throw new XmlException(SR.InvalidXmlDeserializingExtensionData);
                    _nodeType = XmlNodeType.None;
                    _prefix = string.Empty;
                    _ns = string.Empty;
                    _localName = string.Empty;
                    _value = string.Empty;
                    _attributeCount = 0;
                    _readState = ReadState.EndOfFile;
                    return false;
 
                case ExtensionDataNodeType.Xml:
                    // do nothing
                    break;
 
                default:
                    Debug.Fail("ExtensionDataReader in invalid state");
                    throw new SerializationException(SR.InvalidStateInExtensionDataReader);
            }
            _readState = ReadState.Interactive;
            return true;
        }
 
        public override string Name
        {
            get
            {
                if (IsXmlDataNode)
                {
                    return _xmlNodeReader.Name;
                }
                Debug.Fail("ExtensionDataReader Name property should only be called for IXmlSerializable");
                return string.Empty;
            }
        }
 
        public override bool HasValue
        {
            get
            {
                if (IsXmlDataNode)
                {
                    return _xmlNodeReader.HasValue;
                }
                Debug.Fail("ExtensionDataReader HasValue property should only be called for IXmlSerializable");
                return false;
            }
        }
 
        public override string BaseURI
        {
            get
            {
                if (IsXmlDataNode)
                {
                    return _xmlNodeReader.BaseURI;
                }
                Debug.Fail("ExtensionDataReader BaseURI property should only be called for IXmlSerializable");
                return string.Empty;
            }
        }
 
        public override XmlNameTable NameTable
        {
            get
            {
                if (IsXmlDataNode)
                {
                    return _xmlNodeReader.NameTable;
                }
                Debug.Fail("ExtensionDataReader NameTable property should only be called for IXmlSerializable");
                return null;
            }
        }
 
        public override string? GetAttribute(string name)
        {
            if (IsXmlDataNode)
            {
                return _xmlNodeReader.GetAttribute(name);
            }
            Debug.Fail("ExtensionDataReader GetAttribute method should only be called for IXmlSerializable");
            return null;
        }
 
        public override string GetAttribute(int i)
        {
            if (IsXmlDataNode)
            {
                return _xmlNodeReader.GetAttribute(i);
            }
            Debug.Fail("ExtensionDataReader GetAttribute method should only be called for IXmlSerializable");
            return null;
        }
 
        public override bool MoveToAttribute(string name)
        {
            if (IsXmlDataNode)
            {
                return _xmlNodeReader.MoveToAttribute(name);
            }
            Debug.Fail("ExtensionDataReader MoveToAttribute method should only be called for IXmlSerializable");
            return false;
        }
 
        public override void ResolveEntity()
        {
            if (IsXmlDataNode)
            {
                _xmlNodeReader.ResolveEntity();
            }
            else
            {
                Debug.Fail("ExtensionDataReader ResolveEntity method should only be called for IXmlSerializable");
            }
        }
 
        public override bool ReadAttributeValue()
        {
            if (IsXmlDataNode)
            {
                return _xmlNodeReader.ReadAttributeValue();
            }
            Debug.Fail("ExtensionDataReader ReadAttributeValue method should only be called for IXmlSerializable");
            return false;
        }
 
        private void MoveNext(IDataNode? dataNode)
        {
            switch (_internalNodeType)
            {
                case ExtensionDataNodeType.Text:
                case ExtensionDataNodeType.ReferencedElement:
                case ExtensionDataNodeType.NullElement:
                    _internalNodeType = ExtensionDataNodeType.EndElement;
                    return;
                default:
                    Type? dataNodeType = dataNode?.DataType;
                    if (dataNodeType == Globals.TypeOfClassDataNode)
                        MoveNextInClass((ClassDataNode)dataNode!);
                    else if (dataNodeType == Globals.TypeOfCollectionDataNode)
                        MoveNextInCollection((CollectionDataNode)dataNode!);
                    else if (dataNodeType == Globals.TypeOfISerializableDataNode)
                        MoveNextInISerializable((ISerializableDataNode)dataNode!);
                    else if (dataNodeType == Globals.TypeOfXmlDataNode)
                        MoveNextInXml((XmlDataNode)dataNode!);
                    else if (dataNode?.Value != null)
                        MoveToDeserializedObject(dataNode!);
                    else
                    {
                        Debug.Fail("Encountered invalid data node when deserializing unknown data");
                        throw new SerializationException(SR.Format(SR.InvalidStateInExtensionDataReader));
                    }
                    break;
            }
        }
 
        private void SetNextElement(IDataNode? node, string? name, string? ns, string? prefix)
        {
            _internalNodeType = ExtensionDataNodeType.Element;
            _nextElement = GetNextElement();
            _nextElement.localName = name;
            _nextElement.ns = ns;
            _nextElement.prefix = prefix;
            if (node == null)
            {
                _nextElement.attributeCount = 0;
                _nextElement.AddAttribute(Globals.XsiPrefix, Globals.SchemaInstanceNamespace, Globals.XsiNilLocalName, Globals.True);
                _internalNodeType = ExtensionDataNodeType.NullElement;
            }
            else if (!CheckIfNodeHandled(node))
            {
                AddDeserializedDataNode(node);
                node.GetData(_nextElement);
                if (node is XmlDataNode xdn)
                    MoveNextInXml(xdn);
            }
        }
 
        private void AddDeserializedDataNode(IDataNode node)
        {
            if (node.Id != Globals.NewObjectId && (node.Value == null || !node.IsFinalValue))
            {
                _deserializedDataNodes ??= new Queue<IDataNode>();
                _deserializedDataNodes.Enqueue(node);
            }
        }
 
        private bool CheckIfNodeHandled(IDataNode node)
        {
            bool handled = false;
            if (node.Id != Globals.NewObjectId)
            {
                handled = (_cache[node] != null);
                if (handled)
                {
                    _nextElement ??= GetNextElement();
                    _nextElement.attributeCount = 0;
                    _nextElement.AddAttribute(Globals.SerPrefix, Globals.SerializationNamespace, Globals.RefLocalName, node.Id.ToString(NumberFormatInfo.InvariantInfo));
                    _nextElement.AddAttribute(Globals.XsiPrefix, Globals.SchemaInstanceNamespace, Globals.XsiNilLocalName, Globals.True);
                    _internalNodeType = ExtensionDataNodeType.ReferencedElement;
                }
                else
                {
                    _cache.Add(node, node);
                }
            }
            return handled;
        }
 
        private void MoveNextInClass(ClassDataNode dataNode)
        {
            // Two frames above here in Read(), _element is asserted not null.
            Debug.Assert(_element != null);
            if (dataNode.Members != null && _element.childElementIndex < dataNode.Members.Count)
            {
                if (_element.childElementIndex == 0)
                    _context.IncrementItemCount(-dataNode.Members.Count);
 
                ExtensionDataMember member = dataNode.Members[_element.childElementIndex++];
                SetNextElement(member.Value, member.Name, member.Namespace, GetPrefix(member.Namespace));
            }
            else
            {
                _internalNodeType = ExtensionDataNodeType.EndElement;
                _element.childElementIndex = 0;
            }
        }
 
        private void MoveNextInCollection(CollectionDataNode dataNode)
        {
            // Two frames above here in Read(), _element is asserted not null.
            Debug.Assert(_element != null);
            if (dataNode.Items != null && _element.childElementIndex < dataNode.Items.Count)
            {
                if (_element.childElementIndex == 0)
                    _context.IncrementItemCount(-dataNode.Items.Count);
 
                IDataNode? item = dataNode.Items[_element.childElementIndex++];
                SetNextElement(item, dataNode.ItemName, dataNode.ItemNamespace, GetPrefix(dataNode.ItemNamespace));
            }
            else
            {
                _internalNodeType = ExtensionDataNodeType.EndElement;
                _element.childElementIndex = 0;
            }
        }
 
        private void MoveNextInISerializable(ISerializableDataNode dataNode)
        {
            // Two frames above here in Read(), _element is asserted not null.
            Debug.Assert(_element != null);
            if (dataNode.Members != null && _element.childElementIndex < dataNode.Members.Count)
            {
                if (_element.childElementIndex == 0)
                    _context.IncrementItemCount(-dataNode.Members.Count);
 
                ISerializableDataMember member = dataNode.Members[_element.childElementIndex++];
                SetNextElement(member.Value, member.Name, string.Empty, string.Empty);
            }
            else
            {
                _internalNodeType = ExtensionDataNodeType.EndElement;
                _element.childElementIndex = 0;
            }
        }
 
        private void MoveNextInXml(XmlDataNode dataNode)
        {
            if (IsXmlDataNode)
            {
                _xmlNodeReader.Read();
                if (_xmlNodeReader.Depth == 0)
                {
                    _internalNodeType = ExtensionDataNodeType.EndElement;
                    _xmlNodeReader = null;
                }
            }
            else
            {
                _internalNodeType = ExtensionDataNodeType.Xml;
                if (_element == null)
                    _element = _nextElement;
                else
                    PushElement();
 
                Debug.Assert(dataNode.OwnerDocument != null); // OwnerDocument is always set on initialized dataNodes
 
                XmlElement wrapperElement = XmlObjectSerializerReadContext.CreateWrapperXmlElement(dataNode.OwnerDocument,
                    dataNode.XmlAttributes, dataNode.XmlChildNodes, _element?.prefix, _element?.localName, _element?.ns);
                if (_element != null)
                {
                    for (int i = 0; i < _element.attributeCount; i++)
                    {
                        AttributeData a = _element.attributes![i];
                        XmlAttribute xmlAttr = dataNode.OwnerDocument.CreateAttribute(a.prefix, a.localName!, a.ns);
                        xmlAttr.Value = a.value;
                        wrapperElement.Attributes.Append(xmlAttr);
                    }
                }
                _xmlNodeReader = new XmlNodeReader(wrapperElement);
                _xmlNodeReader.Read();
            }
        }
 
        private void MoveToDeserializedObject(IDataNode dataNode)
        {
            Type type = dataNode.DataType;
            bool isTypedNode = true;
            if (type == Globals.TypeOfObject && dataNode.Value != null)
            {
                type = dataNode.Value.GetType();
                if (type == Globals.TypeOfObject)
                {
                    _internalNodeType = ExtensionDataNodeType.EndElement;
                    return;
                }
                isTypedNode = false;
            }
 
            if (!MoveToText(type, dataNode, isTypedNode))
            {
                if (dataNode.IsFinalValue)
                {
                    _internalNodeType = ExtensionDataNodeType.EndElement;
                }
                else
                {
                    throw new XmlException(SR.Format(SR.InvalidDataNode, DataContract.GetClrTypeFullName(type)));
                }
            }
        }
 
        private bool MoveToText(Type type, IDataNode dataNode, bool isTypedNode)
        {
            Debug.Assert(dataNode.Value != null);
 
            bool handled = true;
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Boolean:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<bool>)dataNode).GetValue() : (bool)dataNode.Value);
                    break;
                case TypeCode.Char:
                    _value = XmlConvert.ToString((int)(isTypedNode ? ((DataNode<char>)dataNode).GetValue() : (char)dataNode.Value));
                    break;
                case TypeCode.Byte:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<byte>)dataNode).GetValue() : (byte)dataNode.Value);
                    break;
                case TypeCode.Int16:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<short>)dataNode).GetValue() : (short)dataNode.Value);
                    break;
                case TypeCode.Int32:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<int>)dataNode).GetValue() : (int)dataNode.Value);
                    break;
                case TypeCode.Int64:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<long>)dataNode).GetValue() : (long)dataNode.Value);
                    break;
                case TypeCode.Single:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<float>)dataNode).GetValue() : (float)dataNode.Value);
                    break;
                case TypeCode.Double:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<double>)dataNode).GetValue() : (double)dataNode.Value);
                    break;
                case TypeCode.Decimal:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<decimal>)dataNode).GetValue() : (decimal)dataNode.Value);
                    break;
                case TypeCode.DateTime:
                    DateTime dateTime = isTypedNode ? ((DataNode<DateTime>)dataNode).GetValue() : (DateTime)dataNode.Value;
                    _value = dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", DateTimeFormatInfo.InvariantInfo);
                    break;
                case TypeCode.String:
                    _value = isTypedNode ? ((DataNode<string>)dataNode).GetValue() : (string?)dataNode.Value;
                    break;
                case TypeCode.SByte:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<sbyte>)dataNode).GetValue() : (sbyte)dataNode.Value);
                    break;
                case TypeCode.UInt16:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<ushort>)dataNode).GetValue() : (ushort)dataNode.Value);
                    break;
                case TypeCode.UInt32:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<uint>)dataNode).GetValue() : (uint)dataNode.Value);
                    break;
                case TypeCode.UInt64:
                    _value = XmlConvert.ToString(isTypedNode ? ((DataNode<ulong>)dataNode).GetValue() : (ulong)dataNode.Value);
                    break;
                case TypeCode.Object:
                default:
                    if (type == Globals.TypeOfByteArray)
                    {
                        byte[]? bytes = isTypedNode ? ((DataNode<byte[]>)dataNode).GetValue() : (byte[])dataNode.Value;
                        _value = (bytes == null) ? string.Empty : Convert.ToBase64String(bytes);
                    }
                    else if (type == Globals.TypeOfTimeSpan)
                        _value = XmlConvert.ToString(isTypedNode ? ((DataNode<TimeSpan>)dataNode).GetValue() : (TimeSpan)dataNode.Value);
                    else if (type == Globals.TypeOfGuid)
                    {
                        Guid guid = isTypedNode ? ((DataNode<Guid>)dataNode).GetValue() : (Guid)dataNode.Value;
                        _value = guid.ToString();
                    }
                    else if (type == Globals.TypeOfUri)
                    {
                        Uri uri = isTypedNode ? ((DataNode<Uri>)dataNode).GetValue() : (Uri)dataNode.Value;
                        _value = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
                    }
                    else
                        handled = false;
                    break;
            }
 
            if (handled)
                _internalNodeType = ExtensionDataNodeType.Text;
            return handled;
        }
 
        private void PushElement()
        {
            GrowElementsIfNeeded();
            _elements[_depth++] = _element;
            if (_nextElement == null)
                _element = GetNextElement();
            else
            {
                _element = _nextElement;
                _nextElement = null;
            }
        }
 
        private void PopElement()
        {
            _prefix = _element!.prefix;
            _localName = _element.localName;
            _ns = _element.ns;
 
            if (_depth == 0)
                return;
 
            _depth--;
 
            if (_elements != null)
            {
                _element = _elements[_depth];
            }
        }
 
        [MemberNotNull(nameof(_elements))]
        private void GrowElementsIfNeeded()
        {
            if (_elements == null)
            {
                _elements = new ElementData[8];
            }
            else if (_elements.Length == _depth)
            {
                ElementData[] newElements = new ElementData[_elements.Length * 2];
                Array.Copy(_elements, newElements, _elements.Length);
                _elements = newElements;
            }
        }
 
        private ElementData GetNextElement()
        {
            int nextDepth = _depth + 1;
            return (_elements == null || _elements.Length <= nextDepth || _elements[nextDepth] == null)
                ? new ElementData() : _elements[nextDepth]!;
        }
 
        internal static string GetPrefix(string? ns)
        {
            ns ??= string.Empty;
            string? prefix = (string?)s_nsToPrefixTable[ns];
            if (prefix == null)
            {
                lock (s_prefixLock)
                {
                    prefix = (string?)s_nsToPrefixTable[ns];
                    if (prefix == null)
                    {
                        prefix = (ns.Length == 0) ? string.Empty : "p" + s_nsToPrefixTable.Count;
                        AddPrefix(prefix, ns);
                    }
                }
            }
            return prefix;
        }
 
        private static void AddPrefix(string prefix, string ns)
        {
            s_nsToPrefixTable.Add(ns, prefix);
            s_prefixToNsTable.Add(prefix, ns);
        }
    }
 
    internal sealed class AttributeData
    {
        public string? prefix;
        public string? ns;
        public string? localName;
        public string? value;
    }
 
    internal sealed class ElementData
    {
        public string? localName;
        public string? ns;
        public string? prefix;
        public int attributeCount;
        public AttributeData[]? attributes;
        public IDataNode? dataNode;
        public int childElementIndex;
 
        public void AddAttribute(string prefix, string ns, string name, string? value)
        {
            GrowAttributesIfNeeded();
            AttributeData attribute = attributes[attributeCount] ??= new AttributeData();
            attribute.prefix = prefix;
            attribute.ns = ns;
            attribute.localName = name;
            attribute.value = value;
            attributeCount++;
        }
 
        [MemberNotNull(nameof(attributes))]
        private void GrowAttributesIfNeeded()
        {
            if (attributes == null)
            {
                attributes = new AttributeData[4];
            }
            else if (attributes.Length == attributeCount)
            {
                AttributeData[] newAttributes = new AttributeData[attributes.Length * 2];
                Array.Copy(attributes, newAttributes, attributes.Length);
                attributes = newAttributes;
            }
        }
    }
}