JsonSerializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / JsonSerializer.cs / 1 / JsonSerializer.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a serializer for the Json format.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    using System; 
    using System.Collections;
    using System.Data.Services.Providers; 
    using System.Diagnostics; 
    using System.IO;
    using System.Text; 

    /// 
    /// Provides support for serializing responses in JSON format.
    ///  
    /// For more information, see http://www.json.org/.
    [DebuggerDisplay("Json={absoluteServiceUri}")] 
    internal sealed class JsonSerializer : Serializer 
    {
        /// JsonWriter to write out strings in Json format. 
        private readonly JsonWriter writer;

        /// 
        /// Initializes a new , ready to write out a description. 
        /// 
        /// Description for the requested results. 
        /// Stream to which output should be sent. 
        /// Absolute URI to the service entry point.
        /// Service with configuration and provider from which metadata should be gathered. 
        /// Text encoding for the response.
        /// HTTP ETag header value.
        internal JsonSerializer(
            RequestDescription requestDescription, 
            Stream output,
            Uri absoluteServiceUri, 
            IDataService service, 
            Encoding encoding,
            string httpETagHeaderValue) 
            : base(requestDescription, absoluteServiceUri, service, httpETagHeaderValue)
        {
            Debug.Assert(output != null, "output != null");
            Debug.Assert(encoding != null, "encoding != null"); 

            StreamWriter writer = new StreamWriter(output, encoding); 
            this.writer = new JsonWriter(writer); 
        }
 
        /// Serializes exception information.
        /// Description of exception to serialize.
        public override void WriteException(HandleExceptionArgs args)
        { 
            ErrorHandler.SerializeJsonError(args, this.writer);
        } 
 
        /// Flushes the writer to the underlying stream.
        protected override void Flush() 
        {
            this.writer.Flush();
        }
 
        /// Writes a single top-level element.
        /// Expanded properties for the result. 
        /// Element to write, possibly null. 
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        { 
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");

            this.PushSegmentForRoot();
            this.writer.StartWrapper(); 
            this.WriteElementWithName(expanded, element, this.RequestDescription.ContainerName, this.RequestDescription.ResultUri, true /*topLevel*/);
            this.writer.EndWrapper(); 
            this.PopSegmentName(); 
        }
 
        /// Writes multiple top-level elements, possibly none.
        /// Expanded properties for the result.
        /// Enumerator for elements to write.
        /// Whether  was succesfully advanced to the first element. 
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        { 
            Debug.Assert(elements != null, "elements != null"); 
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");
 
            this.PushSegmentForRoot();
            Uri parentUri = this.RequestDescription.ResultUri;
            this.writer.StartWrapper();
            this.writer.StartArrayScope(); 

            while (hasMoved) 
            { 
                object o = elements.Current;
                if (o != null) 
                {
                    this.WriteElementWithName(expanded, o, null, parentUri, false /*topLevel*/);
                }
 
                hasMoved = elements.MoveNext();
            } 
 
            this.writer.EndScope();
            this.writer.EndWrapper(); 
            this.PopSegmentName();
        }

        /// Writes out the uri for the given element. 
        /// element whose uri needs to be written out.
        /// This method accounts for a written entity on the current segment. 
        protected override void WriteLink(object element) 
        {
            Debug.Assert(element != null, "element != null"); 
            ResourceContainer container = this.GetContainerForCurrent(element);
            Uri uri = Serializer.GetUri(element, this.Provider, container, this.AbsoluteServiceUri);
            this.writer.StartWrapper();
            this.WriteLinkObject(uri); 
            this.writer.EndWrapper();
        } 
 
        /// 
        /// Write out the uri for the given elements. 
        /// 
        /// elements whose uri need to be writtne out
        /// the current state of the enumerator.
        /// This method accounts for each link as a written entity on the current segment. 
        protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved)
        { 
            this.writer.StartWrapper(); 
            this.writer.StartArrayScope();
 
            while (hasMoved)
            {
                object o = elements.Current;
                if (o != null) 
                {
                    ResourceContainer container = this.GetContainerForCurrent(o); 
                    Uri uri = Serializer.GetUri(o, this.Provider, container, this.AbsoluteServiceUri); 
                    this.WriteLinkObject(uri);
                } 

                hasMoved = elements.MoveNext();
            }
 
            this.writer.EndScope();
            this.writer.EndWrapper(); 
        } 

        /// Gets the tag name to be used when writing the specified type. 
        /// Provider with metadata to be used for lookups.
        /// Type of resource to be written.
        /// The tag name to be used when writing he specified type.
        private static string GetTagNameForType(IDataServiceProvider provider, ResourceType resourceType) 
        {
            Debug.Assert(provider != null, "provider != null"); 
            Debug.Assert(resourceType != null, "resourceType != null"); 

            ResourceType elementType = provider.GetRootType(resourceType); 
            string tagName = elementType.Name;
            if (!System.Xml.XmlReader.IsName(tagName))
            {
                tagName = System.Xml.XmlConvert.EncodeName(elementType.Name); 
            }
 
            return tagName; 
        }
 
        /// Write the link uri in the payload.
        /// uri which needs to be written.
        /// This method accounts for a written entity on the current segment.
        private void WriteLinkObject(Uri uri) 
        {
            this.IncrementSegmentResultCount(); 
            this.writer.StartObjectScope(); 
            this.writer.WriteName(XmlConstants.UriElementName);
            this.writer.WriteValue(uri.AbsoluteUri); 
            this.writer.EndScope();
        }

        ///  
        /// Attempts to convert the specified primitive value to a serializable string.
        ///  
        /// Non-null value to convert. 
        private void WritePrimitiveValue(object value)
        { 
            Debug.Assert(value != null, "value != null");

            Type valueType = value.GetType();
            if (typeof(String) == valueType) 
            {
                this.writer.WriteValue((string)value); 
            } 
            else if (typeof(System.Xml.Linq.XElement) == valueType)
            { 
                this.writer.WriteValue(((System.Xml.Linq.XElement)value).ToString(System.Xml.Linq.SaveOptions.None));
            }
            else if (typeof(SByte) == valueType)
            { 
                this.writer.WriteValue((SByte)value);
            } 
            else if (typeof(Boolean) == value.GetType()) 
            {
                this.writer.WriteValue((bool)value); 
            }
            else if (typeof(Byte) == value.GetType())
            {
                this.writer.WriteValue((byte)value); 
            }
            else if (typeof(DateTime) == value.GetType()) 
            { 
                this.writer.WriteValue((DateTime)value);
            } 
            else if (typeof(Decimal) == value.GetType())
            {
                this.writer.WriteValue((Decimal)value);
            } 
            else if (typeof(Double) == value.GetType())
            { 
                this.writer.WriteValue((Double)value); 
            }
            else if (typeof(Guid) == value.GetType()) 
            {
                this.writer.WriteValue((Guid)value);
            }
            else if (typeof(Int16) == value.GetType()) 
            {
                this.writer.WriteValue((Int16)value); 
            } 
            else if (typeof(Int32) == value.GetType())
            { 
                this.writer.WriteValue((Int32)value);
            }
            else if (typeof(Int64) == value.GetType())
            { 
                this.writer.WriteValue((Int64)value);
            } 
            else if (typeof(Single) == value.GetType()) 
            {
                this.writer.WriteValue((Single)value); 
            }
            else if (typeof(byte[]) == value.GetType())
            {
                byte[] byteArray = (byte[])value; 
                string result = Convert.ToBase64String(byteArray, Base64FormattingOptions.None);
                this.writer.WriteValue(result); 
            } 
            else
            { 
                Debug.Assert(typeof(System.Data.Linq.Binary) == value.GetType(), "typeof(Binary) == value.GetType() (" + value.GetType() + ")");
                this.WritePrimitiveValue(((System.Data.Linq.Binary)value).ToArray());
            }
        } 

        /// Writes the ID and uri path information for the specified resource. 
        /// Resource for which URI information should be written. 
        /// type of the resource
        /// uri of the resource for which the metadata is getting written 
        /// The tag name for the resource that was written.
        private string WriteMetadataObject(object resource, ResourceType resourceType, Uri uriPath)
        {
            Debug.Assert(resource != null, "resource != null"); 
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(uriPath != null || resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "uri can be null for complex types"); 
 
            this.writer.WriteName(XmlConstants.JsonMetadataString);
            this.writer.StartObjectScope(); 

            // Write uri value only for entity types
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            { 
                this.writer.WriteName(XmlConstants.JsonUriString);
                this.writer.WriteValue(uriPath.AbsoluteUri); 
 
                // Write the etag property, if the type has etag properties
                string etag = this.GetETagValue(resource); 
                if (etag != null)
                {
                    this.writer.WriteName(XmlConstants.JsonETagString);
                    this.writer.WriteValue(etag); 
                }
            } 
 
            this.writer.WriteName(XmlConstants.JsonTypeString);
 
            // For generic types, we need to remove the assembly qualified names for the argument types
            this.writer.WriteValue(resourceType.FullName);
            this.writer.EndScope();
 
            return GetTagNameForType(this.Provider, resourceType);
        } 
 
        /// Writes an element with an optional specified name.
        /// Expanded properties for the result. 
        /// Element to write, possibly null.
        /// Name of element to write, possibly null.
        /// URI of element to write.
        /// whether the element is a top level element or not. 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801", MessageId = "elementName", Justification = "Pending review")]
        private void WriteElementWithName(IExpandedResult expanded, object element, string elementName, Uri elementUri, bool topLevel) 
        { 
            Debug.Assert(elementName == null || elementName.Length > 0, "elementName == null || elementName.Length > 0");
            Debug.Assert(elementUri != null, "elementUri != null"); 

            if (element == null)
            {
                if (topLevel) 
                {
                    this.writer.StartObjectScope(); 
                    this.writer.WriteName(elementName); 
                    this.WriteNullValue();
                    this.writer.EndScope(); 
                }
                else
                {
                    this.WriteNullValue(); 
                }
            } 
            else 
            {
                IEnumerable enumerableElement; 
                if (WebUtil.IsElementIEnumerable(element, out enumerableElement))
                {
                    Debug.Assert(elementName != null, "elementName != null - only collection properties are handled here");
                    this.writer.StartArrayScope(); 
                    IEnumerator enumerator = enumerableElement.GetEnumerator();
                    try 
                    { 
                        IExpandedResult expandedEnumerator = enumerator as IExpandedResult;
                        while (enumerator.MoveNext()) 
                        {
                            object elementInCollection = enumerator.Current;
                            if (elementInCollection != null)
                            { 
                                if (elementInCollection is IExpandedResult)
                                { 
                                    expandedEnumerator = (IExpandedResult)elementInCollection; 
                                    elementInCollection = expandedEnumerator.ExpandedElement;
                                } 

                                this.WriteElementWithName(expandedEnumerator, elementInCollection, null, elementUri, false /*topLevel*/);
                            }
                        } 
                    }
                    finally 
                    { 
                        WebUtil.Dispose(enumerator);
                    } 

                    this.writer.EndScope();
                    return;
                } 

                Type elementType = element.GetType(); 
                ResourceType resourceType = this.Provider.GetResourceType(elementType); 
                if (resourceType == null)
                { 
                    // Skip this element.
                    return;
                }
 
                switch (resourceType.ResourceTypeKind)
                { 
                    case ResourceTypeKind.ComplexType: 
                        if (topLevel)
                        { 
                            this.writer.StartObjectScope();
                            this.writer.WriteName(elementName);
                        }
 
                        // Non-value complex types may form a cycle.
                        // PERF: we can keep a single element around and save the HashSet initialization 
                        // until we find a second complex type - this saves the allocation on trees 
                        // with shallow (single-level) complex types.
                        if (this.AddToComplexTypeCollection(element)) 
                        {
                            this.WriteComplexTypeProperties(element, resourceType, elementUri);
                            this.RemoveFromComplexTypeCollection(element);
                        } 
                        else
                        { 
                            throw new InvalidOperationException(Strings.DataServiceException_GeneralError); 
                        }
 
                        if (topLevel)
                        {
                            this.writer.EndScope();
                        } 

                        break; 
                    case ResourceTypeKind.EntityType: 
                        this.IncrementSegmentResultCount();
                        this.writer.StartObjectScope(); 
                        Uri entityUri = Serializer.GetUri(element, this.Provider, this.GetContainerForCurrent(element), this.AbsoluteServiceUri);
                        this.WriteMetadataObject(element, resourceType, entityUri);
                        this.WriteResourceProperties(expanded, element, resourceType, entityUri);
                        this.writer.EndScope(); 
                        break;
                    default: 
                        Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive"); 
                        if (topLevel)
                        { 
                            this.writer.StartObjectScope();
                            this.writer.WriteName(elementName);
                            this.WritePrimitiveValue(element);
                            this.writer.EndScope(); 
                        }
                        else 
                        { 
                            this.WritePrimitiveValue(element);
                        } 

                        break;
                }
            } 
        }
 
        /// Writes all the properties of the specified resource. 
        /// Expanded properties for the result.
        /// Resource with properties to write out. 
        /// Type for the specified resource (saves the lookup in this method).
        /// uri of the resource whose properties are getting written
        private void WriteResourceProperties(IExpandedResult expanded, object resource, ResourceType resourceType, Uri uri)
        { 
            Debug.Assert(resource != null, "resource != null");
            Debug.Assert(resourceType != null, "resourceType != null"); 
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be entity type"); 
            Debug.Assert(uri != null, "uri != null");
 
            this.WriteObjectProperties(expanded, resource, resourceType, uri, true);
        }

        /// Writes all the properties of the specified complex type. 
        /// Object of a complex type with properties to be written out.
        /// resource type representing the current object 
        /// uri of the complex type whose properties needs to be written 
        private void WriteComplexTypeProperties(object complexObject, ResourceType resourceType, Uri parentUri)
        { 
            Debug.Assert(complexObject != null, "complexObject != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "resource must be complex type");
            Debug.Assert(resourceType.Type.IsAssignableFrom(complexObject.GetType()), "resourceType.Type.IsAssignableFrom(complexObject.GetType())"); 

            this.writer.StartObjectScope(); 
            this.WriteMetadataObject(complexObject, resourceType, null); 
            this.WriteObjectProperties(null, complexObject, resourceType, parentUri, false);
            this.writer.EndScope(); 
        }

        /// Writes a JSON _deferred element.
        /// uri of the element which is getting deferred 
        private void WriteDeferredContentElement(Uri uri)
        { 
            Debug.Assert(uri != null, "uri != null"); 

            this.writer.StartObjectScope(); 
            this.writer.WriteName(XmlConstants.JsonDeferredString);
            this.writer.StartObjectScope();
            this.writer.WriteName(XmlConstants.JsonUriString);
            this.writer.WriteValue(uri.AbsoluteUri); 
            this.writer.EndScope();
            this.writer.EndScope(); 
        } 

        /// Writes an attribute to indicate that there is no value. 
        private void WriteNullValue()
        {
            this.writer.WriteValue((string)null);
        } 

        /// Writes all the properties of the specified resource or complex object. 
        /// Expanded properties for the result. 
        /// Resource or complex object with properties to write out.
        /// Resource Type representing the given object instance. 
        /// uri of the object whose properties are getting written
        /// true if the specified object is a resource; false otherwise.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801", MessageId = "objectIsResource", Justification = "Pending review")]
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri parentUri, bool objectIsResource) 
        {
            Debug.Assert(customObject != null, "customObject != null"); 
            Debug.Assert(resourceType != null, "customObjectType != null"); 
            Debug.Assert(resourceType.Type == customObject.GetType(), "resourceType.Type == customObject.GetType()");
            Debug.Assert(parentUri != null, "parentUri != null"); 
            Debug.Assert(resourceType.ResourceTypeKind != ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive");

            // We should throw while if there are navigation properties in the derived entity type
            if (this.CurrentContainer != null && this.CurrentContainer.IsEntityDisallowed(resourceType)) 
            {
                throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name)); 
            } 

            foreach (ResourceProperty property in resourceType.Properties) 
            {
                Debug.Assert(
                    objectIsResource || !property.IsOfKind(ResourcePropertyKind.Key),
                    "objectIsResource || property.Kind != ResourcePropertyKind.KeyPrimitive - complex types shouldn't have key properties"); 

                string propertyName = property.Name; 
                this.writer.WriteName(propertyName); 

                // For any navigation property, we just stick the deferred element with the uri 
                // This uri is different from the canonical uri: we just append the property name
                // to the parent uri. We don't want to analyze the nav property value in either case
                bool mayDefer = property.TypeKind == ResourceTypeKind.EntityType;
                if (mayDefer && !this.ShouldExpandSegment(propertyName)) 
                {
                    this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, propertyName)); 
                } 
                else
                { 
                    object propertyValue;
                    IExpandedResult expandedValue = null;
                    if (mayDefer)
                    { 
                        propertyValue = GetExpandedProperty(expanded, customObject, property);
                        expandedValue = propertyValue as IExpandedResult; 
                        if (expandedValue != null) 
                        {
                            propertyValue = expandedValue.ExpandedElement; 
                        }
                    }
                    else
                    { 
                        propertyValue = GetExpandedProperty(null, customObject, property);
                    } 
 
                    this.PushSegmentForProperty(property);
                    this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/); 
                    this.PopSegmentName();
                }
            }
 
#if ASTORIA_OPEN_OBJECT
            if (resourceType.IsOpenType) 
            { 
                foreach (var pair in OpenTypeAttribute.EnumerateOpenProperties(customObject))
                { 
                    string propertyName = pair.Key;
                    object propertyValue = pair.Value;

                    IExpandedResult expandedValue = propertyValue as IExpandedResult; 
                    if (expandedValue != null)
                    { 
                        propertyValue = expandedValue.ExpandedElement; 
                    }
 
                    // Ignore open properties with null values
                    if (propertyValue == null || propertyValue == DBNull.Value)
                    {
                        continue; 
                    }
 
                    // We need to ensure that strings and other primitive types which implement IEnumerable 
                    // are not considered as containing elements.
                    Type propertyValueType = propertyValue.GetType(); 
                    Type propertyElementType = WebUtil.IsPrimitiveType(propertyValueType) ? null : BaseServiceProvider.GetIEnumerableElement(propertyValueType);
                    propertyElementType = propertyElementType ?? propertyValueType;
                    ResourceType propertyResourceType = this.Provider.GetResourceType(propertyElementType);
 
                    if (propertyResourceType == null)
                    { 
                        // Black-listed types are not supported. 
                        continue;
                    } 

                    ResourceTypeKind kind = propertyResourceType.ResourceTypeKind;
                    if (propertyElementType != propertyValueType && kind != ResourceTypeKind.EntityType)
                    { 
                        // Only enumerations of entities are supported.
                        continue; 
                    } 

                    this.writer.WriteName(propertyName); 

                    if (kind != ResourceTypeKind.EntityType || this.ShouldExpandSegment(propertyName))
                    {
                        this.PushSegmentForOpenProperty(propertyName, propertyResourceType); 
                        this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/);
                        this.PopSegmentName(); 
                    } 
                    else
                    { 
                        // For collection properties, we need to add () at the end to make it a valid uri
                        string segmentIdentifier = propertyName;
                        if (propertyElementType != propertyValueType)
                        { 
                            segmentIdentifier += "()";
                        } 
 
                        this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, segmentIdentifier));
                    } 
                }
            }
#endif
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a serializer for the Json format.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    using System; 
    using System.Collections;
    using System.Data.Services.Providers; 
    using System.Diagnostics; 
    using System.IO;
    using System.Text; 

    /// 
    /// Provides support for serializing responses in JSON format.
    ///  
    /// For more information, see http://www.json.org/.
    [DebuggerDisplay("Json={absoluteServiceUri}")] 
    internal sealed class JsonSerializer : Serializer 
    {
        /// JsonWriter to write out strings in Json format. 
        private readonly JsonWriter writer;

        /// 
        /// Initializes a new , ready to write out a description. 
        /// 
        /// Description for the requested results. 
        /// Stream to which output should be sent. 
        /// Absolute URI to the service entry point.
        /// Service with configuration and provider from which metadata should be gathered. 
        /// Text encoding for the response.
        /// HTTP ETag header value.
        internal JsonSerializer(
            RequestDescription requestDescription, 
            Stream output,
            Uri absoluteServiceUri, 
            IDataService service, 
            Encoding encoding,
            string httpETagHeaderValue) 
            : base(requestDescription, absoluteServiceUri, service, httpETagHeaderValue)
        {
            Debug.Assert(output != null, "output != null");
            Debug.Assert(encoding != null, "encoding != null"); 

            StreamWriter writer = new StreamWriter(output, encoding); 
            this.writer = new JsonWriter(writer); 
        }
 
        /// Serializes exception information.
        /// Description of exception to serialize.
        public override void WriteException(HandleExceptionArgs args)
        { 
            ErrorHandler.SerializeJsonError(args, this.writer);
        } 
 
        /// Flushes the writer to the underlying stream.
        protected override void Flush() 
        {
            this.writer.Flush();
        }
 
        /// Writes a single top-level element.
        /// Expanded properties for the result. 
        /// Element to write, possibly null. 
        protected override void WriteTopLevelElement(IExpandedResult expanded, object element)
        { 
            Debug.Assert(this.RequestDescription.IsSingleResult, "this.RequestDescription.SingleResult");

            this.PushSegmentForRoot();
            this.writer.StartWrapper(); 
            this.WriteElementWithName(expanded, element, this.RequestDescription.ContainerName, this.RequestDescription.ResultUri, true /*topLevel*/);
            this.writer.EndWrapper(); 
            this.PopSegmentName(); 
        }
 
        /// Writes multiple top-level elements, possibly none.
        /// Expanded properties for the result.
        /// Enumerator for elements to write.
        /// Whether  was succesfully advanced to the first element. 
        protected override void WriteTopLevelElements(IExpandedResult expanded, IEnumerator elements, bool hasMoved)
        { 
            Debug.Assert(elements != null, "elements != null"); 
            Debug.Assert(!this.RequestDescription.IsSingleResult, "!this.RequestDescription.SingleResult");
 
            this.PushSegmentForRoot();
            Uri parentUri = this.RequestDescription.ResultUri;
            this.writer.StartWrapper();
            this.writer.StartArrayScope(); 

            while (hasMoved) 
            { 
                object o = elements.Current;
                if (o != null) 
                {
                    this.WriteElementWithName(expanded, o, null, parentUri, false /*topLevel*/);
                }
 
                hasMoved = elements.MoveNext();
            } 
 
            this.writer.EndScope();
            this.writer.EndWrapper(); 
            this.PopSegmentName();
        }

        /// Writes out the uri for the given element. 
        /// element whose uri needs to be written out.
        /// This method accounts for a written entity on the current segment. 
        protected override void WriteLink(object element) 
        {
            Debug.Assert(element != null, "element != null"); 
            ResourceContainer container = this.GetContainerForCurrent(element);
            Uri uri = Serializer.GetUri(element, this.Provider, container, this.AbsoluteServiceUri);
            this.writer.StartWrapper();
            this.WriteLinkObject(uri); 
            this.writer.EndWrapper();
        } 
 
        /// 
        /// Write out the uri for the given elements. 
        /// 
        /// elements whose uri need to be writtne out
        /// the current state of the enumerator.
        /// This method accounts for each link as a written entity on the current segment. 
        protected override void WriteLinkCollection(IEnumerator elements, bool hasMoved)
        { 
            this.writer.StartWrapper(); 
            this.writer.StartArrayScope();
 
            while (hasMoved)
            {
                object o = elements.Current;
                if (o != null) 
                {
                    ResourceContainer container = this.GetContainerForCurrent(o); 
                    Uri uri = Serializer.GetUri(o, this.Provider, container, this.AbsoluteServiceUri); 
                    this.WriteLinkObject(uri);
                } 

                hasMoved = elements.MoveNext();
            }
 
            this.writer.EndScope();
            this.writer.EndWrapper(); 
        } 

        /// Gets the tag name to be used when writing the specified type. 
        /// Provider with metadata to be used for lookups.
        /// Type of resource to be written.
        /// The tag name to be used when writing he specified type.
        private static string GetTagNameForType(IDataServiceProvider provider, ResourceType resourceType) 
        {
            Debug.Assert(provider != null, "provider != null"); 
            Debug.Assert(resourceType != null, "resourceType != null"); 

            ResourceType elementType = provider.GetRootType(resourceType); 
            string tagName = elementType.Name;
            if (!System.Xml.XmlReader.IsName(tagName))
            {
                tagName = System.Xml.XmlConvert.EncodeName(elementType.Name); 
            }
 
            return tagName; 
        }
 
        /// Write the link uri in the payload.
        /// uri which needs to be written.
        /// This method accounts for a written entity on the current segment.
        private void WriteLinkObject(Uri uri) 
        {
            this.IncrementSegmentResultCount(); 
            this.writer.StartObjectScope(); 
            this.writer.WriteName(XmlConstants.UriElementName);
            this.writer.WriteValue(uri.AbsoluteUri); 
            this.writer.EndScope();
        }

        ///  
        /// Attempts to convert the specified primitive value to a serializable string.
        ///  
        /// Non-null value to convert. 
        private void WritePrimitiveValue(object value)
        { 
            Debug.Assert(value != null, "value != null");

            Type valueType = value.GetType();
            if (typeof(String) == valueType) 
            {
                this.writer.WriteValue((string)value); 
            } 
            else if (typeof(System.Xml.Linq.XElement) == valueType)
            { 
                this.writer.WriteValue(((System.Xml.Linq.XElement)value).ToString(System.Xml.Linq.SaveOptions.None));
            }
            else if (typeof(SByte) == valueType)
            { 
                this.writer.WriteValue((SByte)value);
            } 
            else if (typeof(Boolean) == value.GetType()) 
            {
                this.writer.WriteValue((bool)value); 
            }
            else if (typeof(Byte) == value.GetType())
            {
                this.writer.WriteValue((byte)value); 
            }
            else if (typeof(DateTime) == value.GetType()) 
            { 
                this.writer.WriteValue((DateTime)value);
            } 
            else if (typeof(Decimal) == value.GetType())
            {
                this.writer.WriteValue((Decimal)value);
            } 
            else if (typeof(Double) == value.GetType())
            { 
                this.writer.WriteValue((Double)value); 
            }
            else if (typeof(Guid) == value.GetType()) 
            {
                this.writer.WriteValue((Guid)value);
            }
            else if (typeof(Int16) == value.GetType()) 
            {
                this.writer.WriteValue((Int16)value); 
            } 
            else if (typeof(Int32) == value.GetType())
            { 
                this.writer.WriteValue((Int32)value);
            }
            else if (typeof(Int64) == value.GetType())
            { 
                this.writer.WriteValue((Int64)value);
            } 
            else if (typeof(Single) == value.GetType()) 
            {
                this.writer.WriteValue((Single)value); 
            }
            else if (typeof(byte[]) == value.GetType())
            {
                byte[] byteArray = (byte[])value; 
                string result = Convert.ToBase64String(byteArray, Base64FormattingOptions.None);
                this.writer.WriteValue(result); 
            } 
            else
            { 
                Debug.Assert(typeof(System.Data.Linq.Binary) == value.GetType(), "typeof(Binary) == value.GetType() (" + value.GetType() + ")");
                this.WritePrimitiveValue(((System.Data.Linq.Binary)value).ToArray());
            }
        } 

        /// Writes the ID and uri path information for the specified resource. 
        /// Resource for which URI information should be written. 
        /// type of the resource
        /// uri of the resource for which the metadata is getting written 
        /// The tag name for the resource that was written.
        private string WriteMetadataObject(object resource, ResourceType resourceType, Uri uriPath)
        {
            Debug.Assert(resource != null, "resource != null"); 
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(uriPath != null || resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "uri can be null for complex types"); 
 
            this.writer.WriteName(XmlConstants.JsonMetadataString);
            this.writer.StartObjectScope(); 

            // Write uri value only for entity types
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            { 
                this.writer.WriteName(XmlConstants.JsonUriString);
                this.writer.WriteValue(uriPath.AbsoluteUri); 
 
                // Write the etag property, if the type has etag properties
                string etag = this.GetETagValue(resource); 
                if (etag != null)
                {
                    this.writer.WriteName(XmlConstants.JsonETagString);
                    this.writer.WriteValue(etag); 
                }
            } 
 
            this.writer.WriteName(XmlConstants.JsonTypeString);
 
            // For generic types, we need to remove the assembly qualified names for the argument types
            this.writer.WriteValue(resourceType.FullName);
            this.writer.EndScope();
 
            return GetTagNameForType(this.Provider, resourceType);
        } 
 
        /// Writes an element with an optional specified name.
        /// Expanded properties for the result. 
        /// Element to write, possibly null.
        /// Name of element to write, possibly null.
        /// URI of element to write.
        /// whether the element is a top level element or not. 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801", MessageId = "elementName", Justification = "Pending review")]
        private void WriteElementWithName(IExpandedResult expanded, object element, string elementName, Uri elementUri, bool topLevel) 
        { 
            Debug.Assert(elementName == null || elementName.Length > 0, "elementName == null || elementName.Length > 0");
            Debug.Assert(elementUri != null, "elementUri != null"); 

            if (element == null)
            {
                if (topLevel) 
                {
                    this.writer.StartObjectScope(); 
                    this.writer.WriteName(elementName); 
                    this.WriteNullValue();
                    this.writer.EndScope(); 
                }
                else
                {
                    this.WriteNullValue(); 
                }
            } 
            else 
            {
                IEnumerable enumerableElement; 
                if (WebUtil.IsElementIEnumerable(element, out enumerableElement))
                {
                    Debug.Assert(elementName != null, "elementName != null - only collection properties are handled here");
                    this.writer.StartArrayScope(); 
                    IEnumerator enumerator = enumerableElement.GetEnumerator();
                    try 
                    { 
                        IExpandedResult expandedEnumerator = enumerator as IExpandedResult;
                        while (enumerator.MoveNext()) 
                        {
                            object elementInCollection = enumerator.Current;
                            if (elementInCollection != null)
                            { 
                                if (elementInCollection is IExpandedResult)
                                { 
                                    expandedEnumerator = (IExpandedResult)elementInCollection; 
                                    elementInCollection = expandedEnumerator.ExpandedElement;
                                } 

                                this.WriteElementWithName(expandedEnumerator, elementInCollection, null, elementUri, false /*topLevel*/);
                            }
                        } 
                    }
                    finally 
                    { 
                        WebUtil.Dispose(enumerator);
                    } 

                    this.writer.EndScope();
                    return;
                } 

                Type elementType = element.GetType(); 
                ResourceType resourceType = this.Provider.GetResourceType(elementType); 
                if (resourceType == null)
                { 
                    // Skip this element.
                    return;
                }
 
                switch (resourceType.ResourceTypeKind)
                { 
                    case ResourceTypeKind.ComplexType: 
                        if (topLevel)
                        { 
                            this.writer.StartObjectScope();
                            this.writer.WriteName(elementName);
                        }
 
                        // Non-value complex types may form a cycle.
                        // PERF: we can keep a single element around and save the HashSet initialization 
                        // until we find a second complex type - this saves the allocation on trees 
                        // with shallow (single-level) complex types.
                        if (this.AddToComplexTypeCollection(element)) 
                        {
                            this.WriteComplexTypeProperties(element, resourceType, elementUri);
                            this.RemoveFromComplexTypeCollection(element);
                        } 
                        else
                        { 
                            throw new InvalidOperationException(Strings.DataServiceException_GeneralError); 
                        }
 
                        if (topLevel)
                        {
                            this.writer.EndScope();
                        } 

                        break; 
                    case ResourceTypeKind.EntityType: 
                        this.IncrementSegmentResultCount();
                        this.writer.StartObjectScope(); 
                        Uri entityUri = Serializer.GetUri(element, this.Provider, this.GetContainerForCurrent(element), this.AbsoluteServiceUri);
                        this.WriteMetadataObject(element, resourceType, entityUri);
                        this.WriteResourceProperties(expanded, element, resourceType, entityUri);
                        this.writer.EndScope(); 
                        break;
                    default: 
                        Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive"); 
                        if (topLevel)
                        { 
                            this.writer.StartObjectScope();
                            this.writer.WriteName(elementName);
                            this.WritePrimitiveValue(element);
                            this.writer.EndScope(); 
                        }
                        else 
                        { 
                            this.WritePrimitiveValue(element);
                        } 

                        break;
                }
            } 
        }
 
        /// Writes all the properties of the specified resource. 
        /// Expanded properties for the result.
        /// Resource with properties to write out. 
        /// Type for the specified resource (saves the lookup in this method).
        /// uri of the resource whose properties are getting written
        private void WriteResourceProperties(IExpandedResult expanded, object resource, ResourceType resourceType, Uri uri)
        { 
            Debug.Assert(resource != null, "resource != null");
            Debug.Assert(resourceType != null, "resourceType != null"); 
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be entity type"); 
            Debug.Assert(uri != null, "uri != null");
 
            this.WriteObjectProperties(expanded, resource, resourceType, uri, true);
        }

        /// Writes all the properties of the specified complex type. 
        /// Object of a complex type with properties to be written out.
        /// resource type representing the current object 
        /// uri of the complex type whose properties needs to be written 
        private void WriteComplexTypeProperties(object complexObject, ResourceType resourceType, Uri parentUri)
        { 
            Debug.Assert(complexObject != null, "complexObject != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "resource must be complex type");
            Debug.Assert(resourceType.Type.IsAssignableFrom(complexObject.GetType()), "resourceType.Type.IsAssignableFrom(complexObject.GetType())"); 

            this.writer.StartObjectScope(); 
            this.WriteMetadataObject(complexObject, resourceType, null); 
            this.WriteObjectProperties(null, complexObject, resourceType, parentUri, false);
            this.writer.EndScope(); 
        }

        /// Writes a JSON _deferred element.
        /// uri of the element which is getting deferred 
        private void WriteDeferredContentElement(Uri uri)
        { 
            Debug.Assert(uri != null, "uri != null"); 

            this.writer.StartObjectScope(); 
            this.writer.WriteName(XmlConstants.JsonDeferredString);
            this.writer.StartObjectScope();
            this.writer.WriteName(XmlConstants.JsonUriString);
            this.writer.WriteValue(uri.AbsoluteUri); 
            this.writer.EndScope();
            this.writer.EndScope(); 
        } 

        /// Writes an attribute to indicate that there is no value. 
        private void WriteNullValue()
        {
            this.writer.WriteValue((string)null);
        } 

        /// Writes all the properties of the specified resource or complex object. 
        /// Expanded properties for the result. 
        /// Resource or complex object with properties to write out.
        /// Resource Type representing the given object instance. 
        /// uri of the object whose properties are getting written
        /// true if the specified object is a resource; false otherwise.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801", MessageId = "objectIsResource", Justification = "Pending review")]
        private void WriteObjectProperties(IExpandedResult expanded, object customObject, ResourceType resourceType, Uri parentUri, bool objectIsResource) 
        {
            Debug.Assert(customObject != null, "customObject != null"); 
            Debug.Assert(resourceType != null, "customObjectType != null"); 
            Debug.Assert(resourceType.Type == customObject.GetType(), "resourceType.Type == customObject.GetType()");
            Debug.Assert(parentUri != null, "parentUri != null"); 
            Debug.Assert(resourceType.ResourceTypeKind != ResourceTypeKind.Primitive, "resourceType.ResourceTypeKind == ResourceTypeKind.Primitive");

            // We should throw while if there are navigation properties in the derived entity type
            if (this.CurrentContainer != null && this.CurrentContainer.IsEntityDisallowed(resourceType)) 
            {
                throw new InvalidOperationException(Strings.BaseServiceProvider_NavigationPropertiesOnDerivedEntityTypesNotSupported(resourceType.FullName, this.CurrentContainer.Name)); 
            } 

            foreach (ResourceProperty property in resourceType.Properties) 
            {
                Debug.Assert(
                    objectIsResource || !property.IsOfKind(ResourcePropertyKind.Key),
                    "objectIsResource || property.Kind != ResourcePropertyKind.KeyPrimitive - complex types shouldn't have key properties"); 

                string propertyName = property.Name; 
                this.writer.WriteName(propertyName); 

                // For any navigation property, we just stick the deferred element with the uri 
                // This uri is different from the canonical uri: we just append the property name
                // to the parent uri. We don't want to analyze the nav property value in either case
                bool mayDefer = property.TypeKind == ResourceTypeKind.EntityType;
                if (mayDefer && !this.ShouldExpandSegment(propertyName)) 
                {
                    this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, propertyName)); 
                } 
                else
                { 
                    object propertyValue;
                    IExpandedResult expandedValue = null;
                    if (mayDefer)
                    { 
                        propertyValue = GetExpandedProperty(expanded, customObject, property);
                        expandedValue = propertyValue as IExpandedResult; 
                        if (expandedValue != null) 
                        {
                            propertyValue = expandedValue.ExpandedElement; 
                        }
                    }
                    else
                    { 
                        propertyValue = GetExpandedProperty(null, customObject, property);
                    } 
 
                    this.PushSegmentForProperty(property);
                    this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/); 
                    this.PopSegmentName();
                }
            }
 
#if ASTORIA_OPEN_OBJECT
            if (resourceType.IsOpenType) 
            { 
                foreach (var pair in OpenTypeAttribute.EnumerateOpenProperties(customObject))
                { 
                    string propertyName = pair.Key;
                    object propertyValue = pair.Value;

                    IExpandedResult expandedValue = propertyValue as IExpandedResult; 
                    if (expandedValue != null)
                    { 
                        propertyValue = expandedValue.ExpandedElement; 
                    }
 
                    // Ignore open properties with null values
                    if (propertyValue == null || propertyValue == DBNull.Value)
                    {
                        continue; 
                    }
 
                    // We need to ensure that strings and other primitive types which implement IEnumerable 
                    // are not considered as containing elements.
                    Type propertyValueType = propertyValue.GetType(); 
                    Type propertyElementType = WebUtil.IsPrimitiveType(propertyValueType) ? null : BaseServiceProvider.GetIEnumerableElement(propertyValueType);
                    propertyElementType = propertyElementType ?? propertyValueType;
                    ResourceType propertyResourceType = this.Provider.GetResourceType(propertyElementType);
 
                    if (propertyResourceType == null)
                    { 
                        // Black-listed types are not supported. 
                        continue;
                    } 

                    ResourceTypeKind kind = propertyResourceType.ResourceTypeKind;
                    if (propertyElementType != propertyValueType && kind != ResourceTypeKind.EntityType)
                    { 
                        // Only enumerations of entities are supported.
                        continue; 
                    } 

                    this.writer.WriteName(propertyName); 

                    if (kind != ResourceTypeKind.EntityType || this.ShouldExpandSegment(propertyName))
                    {
                        this.PushSegmentForOpenProperty(propertyName, propertyResourceType); 
                        this.WriteElementWithName(expandedValue, propertyValue, propertyName, Serializer.AppendEntryToUri(parentUri, propertyName), false /*topLevel*/);
                        this.PopSegmentName(); 
                    } 
                    else
                    { 
                        // For collection properties, we need to add () at the end to make it a valid uri
                        string segmentIdentifier = propertyName;
                        if (propertyElementType != propertyValueType)
                        { 
                            segmentIdentifier += "()";
                        } 
 
                        this.WriteDeferredContentElement(Serializer.AppendEntryToUri(parentUri, segmentIdentifier));
                    } 
                }
            }
#endif
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK