//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Runtime.Serialization { using System; using System.Text; using System.IO; using System.Xml; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Security; // NOTE: XmlReader methods that are not needed have been left un-implemented class ExtensionDataReader : XmlReader { enum ExtensionDataNodeType { None, Element, EndElement, Text, Xml, ReferencedElement, NullElement, } Hashtable cache = new Hashtable(); ElementData[] elements; ElementData element; ElementData nextElement; ReadState readState = ReadState.Initial; ExtensionDataNodeType internalNodeType; XmlNodeType nodeType; int depth; string localName; string ns; string prefix; string value; int attributeCount; int attributeIndex; XmlNodeReader xmlNodeReader; Queue deserializedDataNodes; XmlObjectSerializerReadContext context; [Fx.Tag.SecurityNote(Critical = "Holds static mappings from namespaces to prefixes." + " Static fields are marked SecurityCritical or readonly to prevent data from being modified or leaked to other components in appdomain.")] [SecurityCritical] static Dictionary nsToPrefixTable; [Fx.Tag.SecurityNote(Critical = "Holds static mappings from prefixes to namespaces." + " Static fields are marked SecurityCritical or readonly to prevent data from being modified or leaked to other components in appdomain.")] [SecurityCritical] static Dictionary prefixToNsTable; [Fx.Tag.SecurityNote(Critical = "Initializes information in critical static cache.", Safe = "Cache is initialized with well-known namespace, prefix mappings.")] [SecuritySafeCritical] static ExtensionDataReader() { nsToPrefixTable = new Dictionary(); prefixToNsTable = new Dictionary(); AddPrefix(Globals.XsiPrefix, Globals.SchemaInstanceNamespace); AddPrefix(Globals.SerPrefix, Globals.SerializationNamespace); AddPrefix(String.Empty, String.Empty); } internal ExtensionDataReader(XmlObjectSerializerReadContext context) { this.attributeIndex = -1; this.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); this.element = nextElement; this.nextElement = null; SetElement(); } internal void Reset() { this.localName = null; this.ns = null; this.prefix = null; this.value = null; this.attributeCount = 0; this.attributeIndex = -1; this.depth = 0; this.element = null; this.nextElement = null; this.elements = null; this.deserializedDataNodes = null; } 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 System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.InvalidXmlDeserializingExtensionData))); this.nodeType = XmlNodeType.Attribute; AttributeData attribute = element.attributes[index]; this.localName = attribute.localName; this.ns = attribute.ns; this.prefix = attribute.prefix; this.value = attribute.value; this.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 (this.nodeType != XmlNodeType.Attribute) return false; SetElement(); return true; } void SetElement() { this.nodeType = XmlNodeType.Element; this.localName = element.localName; this.ns = element.ns; this.prefix = element.prefix; this.value = String.Empty; this.attributeCount = element.attributeCount; this.attributeIndex = -1; } [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical static cache to look up namespace given a prefix.", Safe = "Read only access.")] [SecuritySafeCritical] public override string LookupNamespace(string prefix) { if (IsXmlDataNode) return xmlNodeReader.LookupNamespace(prefix); string ns; if (!prefixToNsTable.TryGetValue(prefix, out ns)) return null; return ns; } public override void Skip() { if (IsXmlDataNode) xmlNodeReader.Skip(); else { if (ReadState != ReadState.Interactive) return; MoveToElement(); if (IsElementNode(this.internalNodeType)) { int depth = 1; while (depth != 0) { if (!Read()) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.InvalidXmlDeserializingExtensionData))); if (IsElementNode(this.internalNodeType)) depth++; else if (this.internalNodeType == ExtensionDataNodeType.EndElement) { ReadEndElement(); depth--; } } } else Read(); } } bool IsElementNode(ExtensionDataNodeType nodeType) { return (nodeType == ExtensionDataNodeType.Element || nodeType == ExtensionDataNodeType.ReferencedElement || nodeType == ExtensionDataNodeType.NullElement); } public override void Close() { if (IsXmlDataNode) xmlNodeReader.Close(); else { Reset(); this.readState = ReadState.Closed; } } 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: this.nodeType = XmlNodeType.Text; this.prefix = String.Empty; this.ns = String.Empty; this.localName = String.Empty; this.attributeCount = 0; this.attributeIndex = -1; break; case ExtensionDataNodeType.EndElement: this.nodeType = XmlNodeType.EndElement; this.prefix = String.Empty; this.ns = String.Empty; this.localName = String.Empty; this.value = String.Empty; this.attributeCount = 0; this.attributeIndex = -1; PopElement(); break; case ExtensionDataNodeType.None: if (depth != 0) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.InvalidXmlDeserializingExtensionData))); this.nodeType = XmlNodeType.None; this.prefix = String.Empty; this.ns = String.Empty; this.localName = String.Empty; this.value = String.Empty; this.attributeCount = 0; readState = ReadState.EndOfFile; return false; case ExtensionDataNodeType.Xml: // do nothing break; default: Fx.Assert("ExtensionDataReader in invalid state"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.GetString(SR.InvalidStateInExtensionDataReader))); } readState = ReadState.Interactive; return true; } public override string Name { get { if (IsXmlDataNode) { return xmlNodeReader.Name; } Fx.Assert("ExtensionDataReader Name property should only be called for IXmlSerializable"); return string.Empty; } } public override bool HasValue { get { if (IsXmlDataNode) { return xmlNodeReader.HasValue; } Fx.Assert("ExtensionDataReader HasValue property should only be called for IXmlSerializable"); return false; } } public override string BaseURI { get { if (IsXmlDataNode) { return xmlNodeReader.BaseURI; } Fx.Assert("ExtensionDataReader BaseURI property should only be called for IXmlSerializable"); return string.Empty; } } public override XmlNameTable NameTable { get { if (IsXmlDataNode) { return xmlNodeReader.NameTable; } Fx.Assert("ExtensionDataReader NameTable property should only be called for IXmlSerializable"); return null; } } public override string GetAttribute(string name) { if (IsXmlDataNode) { return xmlNodeReader.GetAttribute(name); } Fx.Assert("ExtensionDataReader GetAttribute method should only be called for IXmlSerializable"); return null; } public override string GetAttribute(int i) { if (IsXmlDataNode) { return xmlNodeReader.GetAttribute(i); } Fx.Assert("ExtensionDataReader GetAttribute method should only be called for IXmlSerializable"); return null; } public override bool MoveToAttribute(string name) { if (IsXmlDataNode) { return xmlNodeReader.MoveToAttribute(name); } Fx.Assert("ExtensionDataReader MoveToAttribute method should only be called for IXmlSerializable"); return false; } public override void ResolveEntity() { if (IsXmlDataNode) { xmlNodeReader.ResolveEntity(); } else { Fx.Assert("ExtensionDataReader ResolveEntity method should only be called for IXmlSerializable"); } } public override bool ReadAttributeValue() { if (IsXmlDataNode) { return xmlNodeReader.ReadAttributeValue(); } Fx.Assert("ExtensionDataReader ReadAttributeValue method should only be called for IXmlSerializable"); return false; } void MoveNext(IDataNode dataNode) { switch (this.internalNodeType) { case ExtensionDataNodeType.Text: case ExtensionDataNodeType.ReferencedElement: case ExtensionDataNodeType.NullElement: this.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 { Fx.Assert("Encountered invalid data node when deserializing unknown data"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.GetString(SR.InvalidStateInExtensionDataReader))); } break; } } void SetNextElement(IDataNode node, string name, string ns, string prefix) { this.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); this.internalNodeType = ExtensionDataNodeType.NullElement; } else if (!CheckIfNodeHandled(node)) { AddDeserializedDataNode(node); node.GetData(nextElement); if (node is XmlDataNode) MoveNextInXml((XmlDataNode)node); } } void AddDeserializedDataNode(IDataNode node) { if (node.Id != Globals.NewObjectId && (node.Value == null || !node.IsFinalValue)) { if (deserializedDataNodes == null) deserializedDataNodes = new Queue(); deserializedDataNodes.Enqueue(node); } } bool CheckIfNodeHandled(IDataNode node) { bool handled = false; if (node.Id != Globals.NewObjectId) { handled = (cache[node] != null); if (handled) { if (nextElement == null) 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); this.internalNodeType = ExtensionDataNodeType.ReferencedElement; } else { cache.Add(node, node); } } return handled; } void MoveNextInClass(ClassDataNode dataNode) { if (dataNode.Members != null && element.childElementIndex < dataNode.Members.Count) { if (element.childElementIndex == 0) this.context.IncrementItemCount(-dataNode.Members.Count); ExtensionDataMember member = dataNode.Members[element.childElementIndex++]; SetNextElement(member.Value, member.Name, member.Namespace, GetPrefix(member.Namespace)); } else { this.internalNodeType = ExtensionDataNodeType.EndElement; element.childElementIndex = 0; } } void MoveNextInCollection(CollectionDataNode dataNode) { if (dataNode.Items != null && element.childElementIndex < dataNode.Items.Count) { if (element.childElementIndex == 0) this.context.IncrementItemCount(-dataNode.Items.Count); IDataNode item = dataNode.Items[element.childElementIndex++]; SetNextElement(item, dataNode.ItemName, dataNode.ItemNamespace, GetPrefix(dataNode.ItemNamespace)); } else { this.internalNodeType = ExtensionDataNodeType.EndElement; element.childElementIndex = 0; } } void MoveNextInISerializable(ISerializableDataNode dataNode) { if (dataNode.Members != null && element.childElementIndex < dataNode.Members.Count) { if (element.childElementIndex == 0) this.context.IncrementItemCount(-dataNode.Members.Count); ISerializableDataMember member = dataNode.Members[element.childElementIndex++]; SetNextElement(member.Value, member.Name, String.Empty, String.Empty); } else { this.internalNodeType = ExtensionDataNodeType.EndElement; element.childElementIndex = 0; } } void MoveNextInXml(XmlDataNode dataNode) { if (IsXmlDataNode) { xmlNodeReader.Read(); if (xmlNodeReader.Depth == 0) { this.internalNodeType = ExtensionDataNodeType.EndElement; xmlNodeReader = null; } } else { internalNodeType = ExtensionDataNodeType.Xml; if (element == null) element = nextElement; else PushElement(); XmlNode wrapperElement = XmlObjectSerializerReadContext.CreateWrapperXmlElement(dataNode.OwnerDocument, dataNode.XmlAttributes, dataNode.XmlChildNodes, element.prefix, element.localName, element.ns); 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(); } } void MoveToDeserializedObject(IDataNode dataNode) { Type type = dataNode.DataType; bool isTypedNode = true; if (type == Globals.TypeOfObject) { type = dataNode.Value.GetType(); if (type == Globals.TypeOfObject) { this.internalNodeType = ExtensionDataNodeType.EndElement; return; } isTypedNode = false; } if (!MoveToText(type, dataNode, isTypedNode)) { if (dataNode.IsFinalValue) { this.internalNodeType = ExtensionDataNodeType.EndElement; } else { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.InvalidDataNode, DataContract.GetClrTypeFullName(type)))); } } } bool MoveToText(Type type, IDataNode dataNode, bool isTypedNode) { bool handled = true; switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (bool)dataNode.Value); break; case TypeCode.Char: this.value = XmlConvert.ToString((int)(isTypedNode ? ((DataNode)dataNode).GetValue() : (char)dataNode.Value)); break; case TypeCode.Byte: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (byte)dataNode.Value); break; case TypeCode.Int16: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (short)dataNode.Value); break; case TypeCode.Int32: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (int)dataNode.Value); break; case TypeCode.Int64: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (long)dataNode.Value); break; case TypeCode.Single: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (float)dataNode.Value); break; case TypeCode.Double: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (double)dataNode.Value); break; case TypeCode.Decimal: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (decimal)dataNode.Value); break; case TypeCode.DateTime: DateTime dateTime = isTypedNode ? ((DataNode)dataNode).GetValue() : (DateTime)dataNode.Value; this.value = dateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK", DateTimeFormatInfo.InvariantInfo); break; case TypeCode.String: this.value = isTypedNode ? ((DataNode)dataNode).GetValue() : (string)dataNode.Value; break; case TypeCode.SByte: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (sbyte)dataNode.Value); break; case TypeCode.UInt16: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (ushort)dataNode.Value); break; case TypeCode.UInt32: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (uint)dataNode.Value); break; case TypeCode.UInt64: this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (ulong)dataNode.Value); break; case TypeCode.Object: default: if (type == Globals.TypeOfByteArray) { byte[] bytes = isTypedNode ? ((DataNode)dataNode).GetValue() : (byte[])dataNode.Value; this.value = (bytes == null) ? String.Empty : Convert.ToBase64String(bytes); } else if (type == Globals.TypeOfTimeSpan) this.value = XmlConvert.ToString(isTypedNode ? ((DataNode)dataNode).GetValue() : (TimeSpan)dataNode.Value); else if (type == Globals.TypeOfGuid) { Guid guid = isTypedNode ? ((DataNode)dataNode).GetValue() : (Guid)dataNode.Value; this.value = guid.ToString(); } else if (type == Globals.TypeOfUri) { Uri uri = isTypedNode ? ((DataNode)dataNode).GetValue() : (Uri)dataNode.Value; this.value = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); } else handled = false; break; } if (handled) this.internalNodeType = ExtensionDataNodeType.Text; return handled; } void PushElement() { GrowElementsIfNeeded(); elements[depth++] = this.element; if (nextElement == null) element = GetNextElement(); else { element = nextElement; nextElement = null; } } void PopElement() { this.prefix = element.prefix; this.localName = element.localName; this.ns = element.ns; if (depth == 0) return; this.depth--; if (elements != null) { this.element = elements[depth]; } } void GrowElementsIfNeeded() { if (elements == null) elements = new ElementData[8]; else if (elements.Length == depth) { ElementData[] newElements = new ElementData[elements.Length * 2]; Array.Copy(elements, 0, newElements, 0, elements.Length); elements = newElements; } } ElementData GetNextElement() { int nextDepth = depth + 1; return (elements == null || elements.Length <= nextDepth || elements[nextDepth] == null) ? new ElementData() : elements[nextDepth]; } [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical static cache to look up prefix given a namespace .", Safe = "Read only access.")] [SecuritySafeCritical] internal static string GetPrefix(string ns) { string prefix; ns = ns ?? String.Empty; if (!nsToPrefixTable.TryGetValue(ns, out prefix)) { lock (nsToPrefixTable) { if (!nsToPrefixTable.TryGetValue(ns, out prefix)) { prefix = (ns == null || ns.Length == 0) ? String.Empty : "p" + nsToPrefixTable.Count; AddPrefix(prefix, ns); } } } return prefix; } [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical static cache to look up prefix given a namespace .", Safe = "Read only access.")] [SecuritySafeCritical] static void AddPrefix(string prefix, string ns) { nsToPrefixTable.Add(ns, prefix); prefixToNsTable.Add(prefix, ns); } } #if USE_REFEMIT public class AttributeData #else internal class AttributeData #endif { public string prefix; public string ns; public string localName; public string value; } #if USE_REFEMIT public class ElementData #else internal class ElementData #endif { 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]; if (attribute == null) attributes[attributeCount] = attribute = new AttributeData(); attribute.prefix = prefix; attribute.ns = ns; attribute.localName = name; attribute.value = value; attributeCount++; } void GrowAttributesIfNeeded() { if (attributes == null) attributes = new AttributeData[4]; else if (attributes.Length == attributeCount) { AttributeData[] newAttributes = new AttributeData[attributes.Length * 2]; Array.Copy(attributes, 0, newAttributes, 0, attributes.Length); attributes = newAttributes; } } } }