private static ODataProperty ReadStringPropertyUnderServerKnob(string payload) { MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(payload)); ODataMessageReaderSettings settings = new ODataMessageReaderSettings(); settings.EnableODataServerBehavior(); ODataAtomInputContext context = new ODataAtomInputContext(ODataFormat.Atom, memoryStream, Encoding.UTF8, settings, false /*readingResponse*/, true /*sync*/, EdmModel, null); var deserializer = new ODataAtomPropertyAndValueDeserializer(context); return deserializer.ReadTopLevelProperty(StringProperty, StringProperty.Type); }
/// <summary> /// Reads the current element's content as an ODataValue. /// </summary> /// <param name="propertyAndValueDeserializer">The property and value deserializer to use to read values in ATOM.</param> /// <param name="expectedType">The expected type of the annotation, may be null if the term is not defined in the model.</param> /// <returns>The deserialized value.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The XML element containing the value to read (also the attributes will be read from it) /// Post-Condition: XmlNodeType.EndElement - The end tag of the element. /// XmlNodeType.Element - The empty element node. /// </remarks> private static ODataValue ReadValueFromElementContent(ODataAtomPropertyAndValueDeserializer propertyAndValueDeserializer, IEdmTypeReference expectedType) { object result = propertyAndValueDeserializer.ReadNonEntityValue( expectedType, null /*duplicatePropertyNamesChecker*/, null /*collectionValidator*/, true /*validateNullValue*/); return(result.ToODataValue()); }
/// <summary> /// Creates a new ATOM annotation parser. /// </summary> /// <param name="inputContext">The input context this annotation reader should use to read annotation elements.</param> /// <param name="propertyAndValueDeserializer">The property and value deserializer to use to read the value of an annotation element.</param> internal ODataAtomAnnotationReader(ODataAtomInputContext inputContext, ODataAtomPropertyAndValueDeserializer propertyAndValueDeserializer) { this.inputContext = inputContext; this.propertyAndValueDeserializer = propertyAndValueDeserializer; BufferingXmlReader xmlReader = this.inputContext.XmlReader; Debug.Assert(xmlReader != null, "xmlReader != null"); Debug.Assert(xmlReader.NameTable != null, "xmlReader.NameTable != null"); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationTargetAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationTermAttribute); xmlReader.NameTable.Add(AtomConstants.AtomTypeAttributeName); xmlReader.NameTable.Add(AtomConstants.ODataNullAttributeName); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationStringAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationBoolAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationDecimalAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationIntAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationFloatAttribute); this.odataMetadataNamespace = xmlReader.NameTable.Add(AtomConstants.ODataMetadataNamespace); this.attributeElementName = xmlReader.NameTable.Add(AtomConstants.ODataAnnotationElementName); }
/// <summary> /// Creates a new ATOM annotation parser. /// </summary> /// <param name="inputContext">The input context this annotation reader should use to read annotation elements.</param> /// <param name="propertyAndValueDeserializer">The property and value deserializer to use to read the value of an annotation element.</param> internal ODataAtomAnnotationReader(ODataAtomInputContext inputContext, ODataAtomPropertyAndValueDeserializer propertyAndValueDeserializer) { this.inputContext = inputContext; this.propertyAndValueDeserializer = propertyAndValueDeserializer; BufferingXmlReader xmlReader = this.inputContext.XmlReader; Debug.Assert(xmlReader != null, "xmlReader != null"); Debug.Assert(xmlReader.NameTable != null, "xmlReader.NameTable != null"); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationTargetAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationTermAttribute); xmlReader.NameTable.Add(AtomConstants.AtomTypeAttributeName); xmlReader.NameTable.Add(AtomConstants.ODataNullAttributeName); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationStringAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationBoolAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationDecimalAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationIntAttribute); xmlReader.NameTable.Add(AtomConstants.ODataAnnotationFloatAttribute); this.odataMetadataNamespace = xmlReader.NameTable.Add(AtomConstants.ODataMetadataNamespace); this.attributeElementName = xmlReader.NameTable.Add(AtomConstants.ODataAnnotationElementName); }
/// <summary> /// Creates a new instance of this class by consuming xml from the given input context. /// </summary> /// <param name="inputContext">The input context to use to create the annotation.</param> /// <param name="propertyAndValueDeserializer">The property and value deserializer to use when reading values in the annotation element content.</param> /// <returns>The <see cref="AtomInstanceAnnotation"/> populated with the information from the 'm:annotation' XML element, as long as the value is a string. Returns null otherwise.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The annotation element to read. /// Post-Condition: XmlNodeType.Any - The node after the end of the annotation element, or the same element as in the pre-condition if the annotation was skipped. /// </remarks> internal static AtomInstanceAnnotation CreateFrom(ODataAtomInputContext inputContext, ODataAtomPropertyAndValueDeserializer propertyAndValueDeserializer) { var xmlReader = inputContext.XmlReader; Debug.Assert(xmlReader != null, "xmlReader != null"); Debug.Assert(xmlReader.NodeType == XmlNodeType.Element, "xmlReader must be positioned on an Element"); Debug.Assert(xmlReader.NameTable != null, "xmlReader.NameTable != null"); Debug.Assert(xmlReader.LocalName == "annotation", "Must be positioned on an annotation element"); string termAttributeValue = null; string targetAttributeValue = null; string typeAttributeValue = null; bool nullAttributePresentAndTrue = false; bool sawMultipleAttributeValueNotations = false; // Notes on "attribute value notation": // Empty elements may have the annotation value specified via an attribute on the annotation element. // Exactly one of the following attributes must be present if this notation is being used: "string", "int", "bool", "float", "decimal". // The value of the annotation is the value of this value-specifying attribute. string attributeValueNotationAttributeName = null; string attributeValueNotationAttributeValue = null; IEdmPrimitiveTypeReference attributeValueNotationTypeReference = null; XmlNameTable xmlNameTable = xmlReader.NameTable; string metadataNamespace = xmlNameTable.Get(AtomConstants.ODataMetadataNamespace); string nullAttributeName = xmlNameTable.Get(AtomConstants.ODataNullAttributeName); string typeAttributeName = xmlNameTable.Get(AtomConstants.AtomTypeAttributeName); string emptyNamespace = xmlNameTable.Get(string.Empty); string termAttributeName = xmlNameTable.Get(AtomConstants.ODataAnnotationTermAttribute); string targetAttributeName = xmlNameTable.Get(AtomConstants.ODataAnnotationTargetAttribute); // Loop through all the attributes and remember the ones specific to annotations. while (xmlReader.MoveToNextAttribute()) { if (xmlReader.NamespaceEquals(metadataNamespace)) { if (xmlReader.LocalNameEquals(typeAttributeName)) { typeAttributeValue = xmlReader.Value; } else if (xmlReader.LocalNameEquals(nullAttributeName)) { nullAttributePresentAndTrue = ODataAtomReaderUtils.ReadMetadataNullAttributeValue(xmlReader.Value); } // Ignore all other attributes in the metadata namespace. // In general, we only fail on reading if we can't make sense of the document any more. Reader should be loose. // If we choose to start recognizing an additional attribute in the metadata namespace later, be careful not to // fail if it doesn't parse correctly (so that we don't cause a breaking change). } else if (xmlReader.NamespaceEquals(emptyNamespace)) { if (xmlReader.LocalNameEquals(termAttributeName)) { termAttributeValue = xmlReader.Value; // Before doing any other validation or further reading, check whether or not to read this annotation according to the filter. if (propertyAndValueDeserializer.MessageReaderSettings.ShouldSkipAnnotation(termAttributeValue)) { xmlReader.MoveToElement(); return(null); } } else if (xmlReader.LocalNameEquals(targetAttributeName)) { targetAttributeValue = xmlReader.Value; } else { // Check if this attribute is one used by attribute value notation. IEdmPrimitiveTypeReference potentialTypeFromAttributeValueNotation = LookupEdmTypeByAttributeValueNotationName(xmlReader.LocalName); if (potentialTypeFromAttributeValueNotation != null) { // If we've already seen an attribute used for attribute value notation, // throw since we don't know which type to use (even if the values are the same). // But don't throw yet, because we might not have encountered the term name yet, // and the annotation filter might say to ignore this annotation (and so we shouldn't throw). if (attributeValueNotationTypeReference != null) { sawMultipleAttributeValueNotations = true; } attributeValueNotationTypeReference = potentialTypeFromAttributeValueNotation; attributeValueNotationAttributeName = xmlReader.LocalName; attributeValueNotationAttributeValue = xmlReader.Value; } } // Ignore all other attributes in the empty namespace. } // Ignore all other attributes in all other namespaces. } xmlReader.MoveToElement(); // The term attribute is required. if (termAttributeValue == null) { throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_MissingTermAttributeOnAnnotationElement); } if (sawMultipleAttributeValueNotations) { throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_MultipleAttributeValueNotationAttributes); } // If this term is defined in the model, look up its type. If the term is not in the model, this will be null. IEdmTypeReference expectedTypeReference = MetadataUtils.LookupTypeOfValueTerm(termAttributeValue, propertyAndValueDeserializer.Model); ODataValue annotationValue; if (nullAttributePresentAndTrue) { // The m:null attribute has precedence over the content of the element, thus if we find m:null='true' we ignore the content of the element. ReaderValidationUtils.ValidateNullValue( propertyAndValueDeserializer.Model, expectedTypeReference, propertyAndValueDeserializer.MessageReaderSettings, /*validateNullValue*/ true, termAttributeValue); annotationValue = new ODataNullValue(); } else if (attributeValueNotationTypeReference != null) { annotationValue = GetValueFromAttributeValueNotation( expectedTypeReference, attributeValueNotationTypeReference, attributeValueNotationAttributeName, attributeValueNotationAttributeValue, typeAttributeValue, xmlReader.IsEmptyElement, propertyAndValueDeserializer.Model, propertyAndValueDeserializer.MessageReaderSettings); } else { annotationValue = ReadValueFromElementContent(propertyAndValueDeserializer, expectedTypeReference); } // Read the end tag (or the start tag if it was an empty element). xmlReader.Read(); return(new AtomInstanceAnnotation( targetAttributeValue, termAttributeValue, annotationValue)); }
private void VerifyPrimitiveValueRoundtrips(object clrValue, string edmTypeName, ODataVersion version, string description) { IEdmModel model = new EdmModel(); IEdmPrimitiveTypeReference typeReference = new EdmPrimitiveTypeReference((IEdmPrimitiveType)model.FindType(edmTypeName), true); MemoryStream stream = new MemoryStream(); using (ODataAtomOutputContext outputContext = new ODataAtomOutputContext( ODataFormat.Atom, new NonDisposingStream(stream), Encoding.UTF8, new ODataMessageWriterSettings() { Version = version }, /*writingResponse*/ true, /*synchronous*/ true, model, /*urlResolver*/ null)) { ODataAtomPropertyAndValueSerializer serializer = new ODataAtomPropertyAndValueSerializer(outputContext); serializer.XmlWriter.WriteStartElement("ValueElement"); serializer.WritePrimitiveValue( clrValue, /*collectionValidator*/ null, typeReference, /*serializationTypeNameAnnotation*/ null); serializer.XmlWriter.WriteEndElement(); } stream.Position = 0; object actualValue; using (ODataAtomInputContext inputContext = new ODataAtomInputContext( ODataFormat.Atom, stream, Encoding.UTF8, new ODataMessageReaderSettings(), /*readingResponse*/ true, /*synchronous*/ true, model, /*urlResolver*/ null)) { ODataAtomPropertyAndValueDeserializer deserializer = new ODataAtomPropertyAndValueDeserializer(inputContext); deserializer.XmlReader.MoveToContent(); actualValue = deserializer.ReadNonEntityValue( typeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true); } if (clrValue is byte[]) { ((byte[])actualValue).Should().Equal((byte[])clrValue, description); } else { actualValue.Should().Be(clrValue, description); } }
/// <summary> /// This method creates and reads the property from the input and /// returns an <see cref="ODataProperty"/> representing the read property. /// </summary> /// <param name="property">The <see cref="IEdmProperty"/> producing the property to be read.</param> /// <param name="expectedPropertyTypeReference">The expected type reference of the property to read.</param> /// <returns>An <see cref="ODataProperty"/> representing the read property.</returns> private ODataProperty ReadPropertyImplementation(IEdmStructuralProperty property, IEdmTypeReference expectedPropertyTypeReference) { ODataAtomPropertyAndValueDeserializer atomPropertyAndValueDeserializer = new ODataAtomPropertyAndValueDeserializer(this); return(atomPropertyAndValueDeserializer.ReadTopLevelProperty(property, expectedPropertyTypeReference)); }
private void VerifyComplexTypeRoundtrip(ODataComplexValue value, string typeName) { var typeReference = new EdmComplexTypeReference((IEdmComplexType)model.FindType(typeName), true); MemoryStream stream = new MemoryStream(); using (ODataAtomOutputContext outputContext = new ODataAtomOutputContext( ODataFormat.Atom, new NonDisposingStream(stream), Encoding.UTF8, new ODataMessageWriterSettings() { Version = ODataVersion.V4 }, /*writingResponse*/ true, /*synchronous*/ true, model, /*urlResolver*/ null)) { ODataAtomPropertyAndValueSerializer serializer = new ODataAtomPropertyAndValueSerializer(outputContext); serializer.XmlWriter.WriteStartElement("ValueElement"); serializer.WriteComplexValue( value, typeReference, /*isOpenPropertyType*/ false, /*isWritingCollection*/ false, /*beforeValueAction*/ null, /*afterValueAction*/ null, new DuplicatePropertyNamesChecker(false, false), /*collectionValidator*/ null, /*projectedProperties*/ null); serializer.XmlWriter.WriteEndElement(); } stream.Position = 0; object actualValue; using (ODataAtomInputContext inputContext = new ODataAtomInputContext( ODataFormat.Atom, stream, Encoding.UTF8, new ODataMessageReaderSettings(), /*readingResponse*/ true, /*synchronous*/ true, model, /*urlResolver*/ null)) { ODataAtomPropertyAndValueDeserializer deserializer = new ODataAtomPropertyAndValueDeserializer(inputContext); deserializer.XmlReader.MoveToContent(); actualValue = deserializer.ReadNonEntityValue( typeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true); } TestUtils.AssertODataValueAreEqual(actualValue as ODataValue, value); }
/// <summary> /// This method creates and reads the property from the input and /// returns an <see cref="ODataProperty"/> representing the read property. /// </summary> /// <param name="property">The <see cref="IEdmProperty"/> producing the property to be read.</param> /// <param name="expectedPropertyTypeReference">The expected type reference of the property to read.</param> /// <returns>An <see cref="ODataProperty"/> representing the read property.</returns> private ODataProperty ReadPropertyImplementation(IEdmStructuralProperty property, IEdmTypeReference expectedPropertyTypeReference) { ODataAtomPropertyAndValueDeserializer atomPropertyAndValueDeserializer = new ODataAtomPropertyAndValueDeserializer(this); return atomPropertyAndValueDeserializer.ReadTopLevelProperty(property, expectedPropertyTypeReference); }
/// <summary> /// Reads the current element's content as an ODataValue. /// </summary> /// <param name="propertyAndValueDeserializer">The property and value deserializer to use to read values in ATOM.</param> /// <param name="expectedType">The expected type of the annotation, may be null if the term is not defined in the model.</param> /// <returns>The deserialized value.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The XML element containing the value to read (also the attributes will be read from it) /// Post-Condition: XmlNodeType.EndElement - The end tag of the element. /// XmlNodeType.Element - The empty element node. /// </remarks> private static ODataValue ReadValueFromElementContent(ODataAtomPropertyAndValueDeserializer propertyAndValueDeserializer, IEdmTypeReference expectedType) { object result = propertyAndValueDeserializer.ReadNonEntityValue( expectedType, null /*duplicatePropertyNamesChecker*/, null /*collectionValidator*/, true /*validateNullValue*/); return result.ToODataValue(); }
/// <summary> /// Creates a new instance of this class by consuming xml from the given input context. /// </summary> /// <param name="inputContext">The input context to use to create the annotation.</param> /// <param name="propertyAndValueDeserializer">The property and value deserializer to use when reading values in the annotation element content.</param> /// <returns>The <see cref="AtomInstanceAnnotation"/> populated with the information from the 'm:annotation' XML element, as long as the value is a string. Returns null otherwise.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The annotation element to read. /// Post-Condition: XmlNodeType.Any - The node after the end of the annotation element, or the same element as in the pre-condition if the annotation was skipped. /// </remarks> internal static AtomInstanceAnnotation CreateFrom(ODataAtomInputContext inputContext, ODataAtomPropertyAndValueDeserializer propertyAndValueDeserializer) { var xmlReader = inputContext.XmlReader; Debug.Assert(xmlReader != null, "xmlReader != null"); Debug.Assert(xmlReader.NodeType == XmlNodeType.Element, "xmlReader must be positioned on an Element"); Debug.Assert(xmlReader.NameTable != null, "xmlReader.NameTable != null"); Debug.Assert(xmlReader.LocalName == "annotation", "Must be positioned on an annotation element"); string termAttributeValue = null; string targetAttributeValue = null; string typeAttributeValue = null; bool nullAttributePresentAndTrue = false; bool sawMultipleAttributeValueNotations = false; // Notes on "attribute value notation": // Empty elements may have the annotation value specified via an attribute on the annotation element. // Exactly one of the following attributes must be present if this notation is being used: "string", "int", "bool", "float", "decimal". // The value of the annotation is the value of this value-specifying attribute. string attributeValueNotationAttributeName = null; string attributeValueNotationAttributeValue = null; IEdmPrimitiveTypeReference attributeValueNotationTypeReference = null; XmlNameTable xmlNameTable = xmlReader.NameTable; string metadataNamespace = xmlNameTable.Get(AtomConstants.ODataMetadataNamespace); string nullAttributeName = xmlNameTable.Get(AtomConstants.ODataNullAttributeName); string typeAttributeName = xmlNameTable.Get(AtomConstants.AtomTypeAttributeName); string emptyNamespace = xmlNameTable.Get(string.Empty); string termAttributeName = xmlNameTable.Get(AtomConstants.ODataAnnotationTermAttribute); string targetAttributeName = xmlNameTable.Get(AtomConstants.ODataAnnotationTargetAttribute); // Loop through all the attributes and remember the ones specific to annotations. while (xmlReader.MoveToNextAttribute()) { if (xmlReader.NamespaceEquals(metadataNamespace)) { if (xmlReader.LocalNameEquals(typeAttributeName)) { typeAttributeValue = xmlReader.Value; } else if (xmlReader.LocalNameEquals(nullAttributeName)) { nullAttributePresentAndTrue = ODataAtomReaderUtils.ReadMetadataNullAttributeValue(xmlReader.Value); } // Ignore all other attributes in the metadata namespace. // In general, we only fail on reading if we can't make sense of the document any more. Reader should be loose. // If we choose to start recognizing an additional attribute in the metadata namespace later, be careful not to // fail if it doesn't parse correctly (so that we don't cause a breaking change). } else if (xmlReader.NamespaceEquals(emptyNamespace)) { if (xmlReader.LocalNameEquals(termAttributeName)) { termAttributeValue = xmlReader.Value; // Before doing any other validation or further reading, check whether or not to read this annotation according to the filter. if (propertyAndValueDeserializer.MessageReaderSettings.ShouldSkipAnnotation(termAttributeValue)) { xmlReader.MoveToElement(); return null; } } else if (xmlReader.LocalNameEquals(targetAttributeName)) { targetAttributeValue = xmlReader.Value; } else { // Check if this attribute is one used by attribute value notation. IEdmPrimitiveTypeReference potentialTypeFromAttributeValueNotation = LookupEdmTypeByAttributeValueNotationName(xmlReader.LocalName); if (potentialTypeFromAttributeValueNotation != null) { // If we've already seen an attribute used for attribute value notation, // throw since we don't know which type to use (even if the values are the same). // But don't throw yet, because we might not have encountered the term name yet, // and the annotation filter might say to ignore this annotation (and so we shouldn't throw). if (attributeValueNotationTypeReference != null) { sawMultipleAttributeValueNotations = true; } attributeValueNotationTypeReference = potentialTypeFromAttributeValueNotation; attributeValueNotationAttributeName = xmlReader.LocalName; attributeValueNotationAttributeValue = xmlReader.Value; } } // Ignore all other attributes in the empty namespace. } // Ignore all other attributes in all other namespaces. } xmlReader.MoveToElement(); // The term attribute is required. if (termAttributeValue == null) { throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_MissingTermAttributeOnAnnotationElement); } if (sawMultipleAttributeValueNotations) { throw new ODataException(ODataErrorStrings.AtomInstanceAnnotation_MultipleAttributeValueNotationAttributes); } // If this term is defined in the model, look up its type. If the term is not in the model, this will be null. IEdmTypeReference expectedTypeReference = MetadataUtils.LookupTypeOfValueTerm(termAttributeValue, propertyAndValueDeserializer.Model); ODataValue annotationValue; if (nullAttributePresentAndTrue) { // The m:null attribute has precedence over the content of the element, thus if we find m:null='true' we ignore the content of the element. ReaderValidationUtils.ValidateNullValue( propertyAndValueDeserializer.Model, expectedTypeReference, propertyAndValueDeserializer.MessageReaderSettings, /*validateNullValue*/ true, termAttributeValue); annotationValue = new ODataNullValue(); } else if (attributeValueNotationTypeReference != null) { annotationValue = GetValueFromAttributeValueNotation( expectedTypeReference, attributeValueNotationTypeReference, attributeValueNotationAttributeName, attributeValueNotationAttributeValue, typeAttributeValue, xmlReader.IsEmptyElement, propertyAndValueDeserializer.Model, propertyAndValueDeserializer.MessageReaderSettings); } else { annotationValue = ReadValueFromElementContent(propertyAndValueDeserializer, expectedTypeReference); } // Read the end tag (or the start tag if it was an empty element). xmlReader.Read(); return new AtomInstanceAnnotation( targetAttributeValue, termAttributeValue, annotationValue); }