/// <summary> /// Deserializes the attributes from the <paramref name="reader"/> and sets values on <paramref name="element"/> /// </summary> /// <param name="reader">Current content reader.</param> /// <param name="currentRoot">Segment which has child attribute segments.</param> /// <param name="element">Current object.</param> /// <param name="resourceType">Resource type of <paramref name="element"/></param> private void DeserializeAttributes(XmlReader reader, EpmTargetPathSegment currentRoot, object element, ResourceType resourceType) { foreach (var attributeSegment in currentRoot.SubSegments.Where(s => s.IsAttribute)) { String attribValue = WebUtil.GetAttributeEx(reader, attributeSegment.SegmentName.Substring(1), attributeSegment.SegmentNamespaceUri); if (attribValue != null) { if (!EpmContentDeSerializerBase.Match(attributeSegment, this.PropertiesApplied)) { resourceType.SetEpmValue(attributeSegment, element, attribValue, this); } } } }
/// <summary> /// Given a collection of <paramref name="segments"/> corresponding to a property access path /// on the <paramref name="element"/> object, sets the <paramref name="propertyValue"/> on the property /// </summary> /// <param name="segments">Property access path where each element is a property name</param> /// <param name="resourceType">Resource type for which to set the property</param> /// <param name="element">Object on which to set property</param> /// <param name="propertyValue">Value of property</param> /// <param name="currentIndex">Index of the current segment being looked at</param> /// <param name="deserializer">Current deserializer</param> internal void SetPropertyValueFromPath( String[] segments, ResourceType resourceType, object element, object propertyValue, int currentIndex, EpmContentDeSerializerBase deserializer) { String currentSegment = segments[currentIndex]; ResourceProperty clientProp = resourceType.TryResolvePropertyName(currentSegment); ResourceType propertyType; if (clientProp == null && resourceType.IsOpenType == false) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(currentSegment, resourceType.FullName)); } // If this is a open property OR we do not have to do type conversion for primitive types, // read the type from the payload. if (clientProp == null || (!deserializer.Service.Configuration.EnableTypeConversion && clientProp.ResourceType.ResourceTypeKind == ResourceTypeKind.Primitive)) { String foundTypeName = deserializer.PropertiesApplied.MapPropertyToType(String.Join("/", segments, 0, currentIndex + 1)); if (foundTypeName != null) { propertyType = WebUtil.TryResolveResourceType(deserializer.Service.Provider, foundTypeName); if (propertyType == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(foundTypeName)); } if (propertyType.ResourceTypeKind == ResourceTypeKind.EntityType) { throw DataServiceException.CreateBadRequestError( Strings.PlainXml_EntityTypeNotSupported(propertyType.FullName)); } } else { propertyType = ResourceType.PrimitiveStringResourceType; } } else { propertyType = clientProp.ResourceType; } object currentValue; // Re-construct the source path to add the newly applied property string sourcePath = string.Join("/", segments, 0, currentIndex + 1); switch (propertyType.ResourceTypeKind) { case ResourceTypeKind.ComplexType: if (!deserializer.PropertiesApplied.Lookup(sourcePath)) { // Complex types are treated as atomic and we never allow merging of properties belonging to // a complex type. In other words, we either update the whole complex type or not at all, // never just a subset of its properties. If the complex property has not been applied yet // we create a new instance then apply its property mappings. currentValue = deserializer.Updatable.CreateResource(null, propertyType.FullName); ResourceType.SetEpmProperty(element, currentSegment, currentValue, sourcePath, deserializer); } else { // We've already created a new instance of the complex property by now, reuse the same instance. currentValue = deserializer.Updatable.GetValue(element, currentSegment); Debug.Assert(currentValue != null, "currentValue != null -- we should never be here if the complex property were null."); } this.SetPropertyValueFromPath(segments, propertyType, currentValue, propertyValue, ++currentIndex, deserializer); break; case ResourceTypeKind.EntityType: throw DataServiceException.CreateBadRequestError( Strings.PlainXml_NavigationPropertyNotSupported(clientProp.Name)); default: Debug.Assert( propertyType.ResourceTypeKind == ResourceTypeKind.Primitive, "property.TypeKind == ResourceTypeKind.Primitive -- metadata shouldn't return " + propertyType.ResourceTypeKind); currentValue = PlainXmlDeserializer.ConvertValuesForXml(propertyValue, currentSegment, propertyType.InstanceType); // Do not try to update the property if it is a key property if (!deserializer.IsUpdateOperation || clientProp == null || !clientProp.IsOfKind(ResourcePropertyKind.Key)) { ResourceType.SetEpmProperty(element, currentSegment, currentValue, sourcePath, deserializer); } break; } }
/// <summary> /// Sets the value <paramref name="propertyValue"/> on the <paramref name="currentValue"/> object /// </summary> /// <param name="currentSegment">Target path segment containing the corresponding attribute information</param> /// <param name="currentValue">Object on which to set property</param> /// <param name="propertyValue">Value to be set</param> /// <param name="deserializer">Current deserializer</param> internal void SetEpmValue(EpmTargetPathSegment currentSegment, Object currentValue, object propertyValue, EpmContentDeSerializerBase deserializer) { if (currentSegment.EpmInfo.Attribute.KeepInContent == false) { this.SetPropertyValueFromPath( currentSegment.EpmInfo.Attribute.SourcePath.Split('/'), this, currentValue, propertyValue, 0, deserializer); } }
/// <summary> /// Sets a mapped property value and mark its source path as applied /// </summary> /// <param name="element">Object on which to set the property</param> /// <param name="propertyName">Name of the property</param> /// <param name="propertyValue">Value of the property</param> /// <param name="sourcePath">Source mapping path for the property to be set</param> /// <param name="deserializer">Current deserializer</param> private static void SetEpmProperty(object element, string propertyName, object propertyValue, string sourcePath, EpmContentDeSerializerBase deserializer) { deserializer.Updatable.SetValue(element, propertyName, propertyValue); deserializer.PropertiesApplied.AddAppliedProperty(sourcePath, false); }
/// <summary>Used for deserializing each of syndication specific content nodes</summary> /// <param name="currentRoot">Node in the target path being processed</param> /// <param name="resourceType">ResourceType</param> /// <param name="element">object being deserialized</param> private void DeSerialize(EpmTargetPathSegment currentRoot, ResourceType resourceType, object element) { foreach (EpmTargetPathSegment newRoot in currentRoot.SubSegments) { if (newRoot.HasContent) { if (!EpmContentDeSerializerBase.Match(newRoot, this.PropertiesApplied)) { switch (newRoot.EpmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.AuthorEmail: if (this.Item.Authors.Count > 0) { resourceType.SetEpmValue(newRoot, element, this.Item.Authors[0].Email, this); } break; case SyndicationItemProperty.AuthorName: if (this.Item.Authors.Count > 0) { resourceType.SetEpmValue(newRoot, element, this.Item.Authors[0].Name, this); } break; case SyndicationItemProperty.AuthorUri: if (this.Item.Authors.Count > 0) { resourceType.SetEpmValue(newRoot, element, this.Item.Authors[0].Uri, this); } break; case SyndicationItemProperty.ContributorEmail: if (this.Item.Contributors.Count > 0) { resourceType.SetEpmValue(newRoot, element, this.Item.Contributors[0].Email, this); } break; case SyndicationItemProperty.ContributorName: if (this.Item.Contributors.Count > 0) { resourceType.SetEpmValue(newRoot, element, this.Item.Contributors[0].Name, this); } break; case SyndicationItemProperty.ContributorUri: if (this.Item.Contributors.Count > 0) { resourceType.SetEpmValue(newRoot, element, this.Item.Contributors[0].Uri, this); } break; case SyndicationItemProperty.Updated: // If this.Item.LastUpdatedTime == DateTimeOffset.MinValue we assume that the date has not been provided // by the user. This is the same assumption Syndication Api does (see Atom10FeedFormatter.WriteItemContents()). // If the date was not provided by the user we should not touch it - otherwise the response will not be // compatible with response sent for the same request and the same resource type but having KeepInContent set to true if (this.Item.LastUpdatedTime > DateTimeOffset.MinValue) { resourceType.SetEpmValue(newRoot, element, XmlConvert.ToString(this.Item.LastUpdatedTime), this); } break; case SyndicationItemProperty.Published: if (this.Item.PublishDate > DateTimeOffset.MinValue) { resourceType.SetEpmValue(newRoot, element, XmlConvert.ToString(this.Item.PublishDate), this); } break; case SyndicationItemProperty.Rights: if (this.Item.Copyright != null) { resourceType.SetEpmValue(newRoot, element, this.Item.Copyright.Text, this); } break; case SyndicationItemProperty.Summary: if (this.Item.Summary != null) { resourceType.SetEpmValue(newRoot, element, this.Item.Summary.Text, this); } break; case SyndicationItemProperty.Title: if (this.Item.Title != null) { resourceType.SetEpmValue(newRoot, element, this.Item.Title.Text, this); } break; default: Debug.Fail("Unhandled SyndicationItemProperty enum value - should never get here."); break; } } } else { this.DeSerialize(newRoot, resourceType, element); } } }
/// <summary>Called internally to deserialize each <see cref="SyndicationElementExtension"/></summary> /// <param name="reader">XmlReader for current extension</param> /// <param name="currentRoot">Node in the target path being processed</param> /// <param name="resourceType">ResourceType</param> /// <param name="element">object being deserialized</param> private void DeSerialize(XmlReader reader, EpmTargetPathSegment currentRoot, ResourceType resourceType, object element) { EpmValueBuilder currentValue = new EpmValueBuilder(); do { switch (reader.NodeType) { case XmlNodeType.Element: if (currentRoot.HasContent) { // Throw an exception that we hit mixed-content. // <contentElement>value<someElement /></contentElement> // <contentElement><someElement />value</contentElement> throw DataServiceException.CreateBadRequestError(Strings.EpmDeserialize_MixedContent(resourceType.FullName)); } String elementName = reader.LocalName; String namespaceUri = reader.NamespaceURI; EpmTargetPathSegment newRoot = currentRoot.SubSegments .SingleOrDefault(s => s.SegmentNamespaceUri == namespaceUri && s.SegmentName == elementName); if (newRoot == null) { WebUtil.SkipToEnd(reader, elementName, namespaceUri); continue; } currentRoot = newRoot; this.DeserializeAttributes(reader, currentRoot, element, resourceType); if (currentRoot.HasContent) { if (reader.IsEmptyElement) { if (!EpmContentDeSerializerBase.Match(currentRoot, this.PropertiesApplied)) { resourceType.SetEpmValue(currentRoot, element, String.Empty, this); } currentRoot = currentRoot.ParentSegment; } } break; case XmlNodeType.CDATA: case XmlNodeType.Text: case XmlNodeType.SignificantWhitespace: if (!currentRoot.HasContent) { // Throw an exception that we hit mixed-content. // <noContentElement>value<contentElement>value</contentElement></noContentElement> // <noContentElement><contentElement>value</contentElement>value</noContentElement> throw DataServiceException.CreateBadRequestError(Strings.EpmDeserialize_MixedContent(resourceType.FullName)); } currentValue.Append(reader.Value); break; case XmlNodeType.EndElement: if (currentRoot.HasContent) { if (!EpmContentDeSerializerBase.Match(currentRoot, this.PropertiesApplied)) { resourceType.SetEpmValue(currentRoot, element, currentValue.Value, this); } } currentRoot = currentRoot.ParentSegment; currentValue.Reset(); break; case XmlNodeType.Comment: case XmlNodeType.Whitespace: break; case XmlNodeType.None: case XmlNodeType.XmlDeclaration: case XmlNodeType.Attribute: case XmlNodeType.EndEntity: case XmlNodeType.EntityReference: case XmlNodeType.Entity: case XmlNodeType.Document: case XmlNodeType.DocumentType: case XmlNodeType.DocumentFragment: case XmlNodeType.Notation: case XmlNodeType.ProcessingInstruction: throw DataServiceException.CreateBadRequestError(Strings.EpmDeserialize_InvalidXmlEntity); } }while (currentRoot.ParentSegment != null && reader.Read()); }