예제 #1
0
        /// <summary>
        /// Converts the given JSON string value to the expected type as per OData conversion rules for JSON values.
        /// </summary>
        /// <param name="stringValue">String value to the converted.</param>
        /// <param name="targetType">Target type to which the string value needs to be converted.</param>
        /// <returns>Object which is in sync with the target type.</returns>
        private static object ConvertStringValue(string stringValue, Type targetType)
        {
            if (targetType == typeof(byte[]))
            {
                return(Convert.FromBase64String(stringValue));
            }

            if (targetType == typeof(Guid))
            {
                return(new Guid(stringValue));
            }

            // Convert.ChangeType does not support TimeSpan.
            if (targetType == typeof(TimeSpan))
            {
                return(XmlConvert.ToTimeSpan(stringValue));
            }

            // DateTime needs to be read using the XML rules (as per the JSON Light spec).
            if (targetType == typeof(DateTime))
            {
                return(PlatformHelper.ConvertStringToDateTime(stringValue));
            }

            // DateTimeOffset needs to be read using the XML rules (as per the JSON Light spec).
            if (targetType == typeof(DateTimeOffset))
            {
                return(PlatformHelper.ConvertStringToDateTimeOffset(stringValue));
            }

            // For string types, we support conversion to all possible primitive types
            return(Convert.ChangeType(stringValue, targetType, CultureInfo.InvariantCulture));
        }
예제 #2
0
        /// <summary>
        /// Converts a string to a DateTimeOffset value.
        /// </summary>
        /// <param name="text">String text to convert.</param>
        /// <param name="targetValue">After invocation, converted value.</param>
        /// <returns>true if the value was converted; false otherwise.</returns>
        /// <remarks>Copy of WebConvert.TryKeyStringToDateTimeOffset.</remarks>
        internal static bool ConvertUriStringToDateTimeOffset(string text, out DateTimeOffset targetValue)
        {
            targetValue = default(DateTimeOffset);

            try
            {
                targetValue = PlatformHelper.ConvertStringToDateTimeOffset(text);
                return(true);
            }
            catch (FormatException exception)
            {
                // This means it is a string similar to DateTimeOffset String, but cannot be parsed as DateTimeOffset and could not be a digit or GUID .etc.
                Match m = PlatformHelper.PotentialDateTimeOffsetValidator.Match(text);
                if (m.Success)
                {
                    // The format should be exactly "yyyy-mm-ddThh:mm:ss('.'s+)?(zzzzzz)?" and each field value is within valid range
                    throw new ODataException(Strings.UriUtils_DateTimeOffsetInvalidFormat(text), exception);
                }

                return(false);
            }
            catch (ArgumentOutOfRangeException exception)
            {
                // This means the timezone number is bigger than 14:00, inclusive exception has detail exception.
                throw new ODataException(Strings.UriUtils_DateTimeOffsetInvalidFormat(text), exception);
            }
        }
예제 #3
0
        /// <summary>
        /// Reads the element value as DateTimeOffset value.
        /// </summary>
        /// <returns>The DateTimeOffset value of the element.</returns>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - the element which value to read.
        /// Post-Condition: Any                 - the node after the element.
        ///
        /// This method is not used in WCF DS client mode.
        /// </remarks>
        protected DateTimeOffset?ReadAtomDateConstruct()
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(!this.UseClientFormatBehavior, "ReadAtomDateConstruct must not be called in WCF DS client mode.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "The element must be in ATOM namespace for this method work.");
            Debug.Assert(
                this.XmlReader.LocalName == AtomConstants.AtomUpdatedElementName ||
                this.XmlReader.LocalName == AtomConstants.AtomPublishedElementName,
                "Only atom:updated and atom:published elements are supported by this method.");

            // TODO: Note that OData V3 doesn't support m:null for Updated and Published, because syndication API didn't allow us to do so
            // Now with ODataLib we might want to reenable this
            string stringValue = this.ReadElementStringValue();

            // The following algorithm is a copy of the algorithm in Syndication API used for parsing the DateTimeOffset
            // The original algorithm failed if the string was shorter than 20 characters and if both of the
            // DateTimeOffset.TryParseExact calls failed. This version in all those cases tried the XmlConvert.ToDateTimeOffset
            // so that we get compatibility with the XSD spec as well (it's relaxing change).
            // We need to use this algorithm instead of simple XmlConvert.ToDateTimeOffset since WCF DS server used this
            // and it seems more allowing in certain cases.
            stringValue = stringValue.Trim();
            if (stringValue.Length >= 20)
            {
                if (stringValue[19] == '.')
                {
                    int startIndex = 20;
                    while ((stringValue.Length > startIndex) && char.IsDigit(stringValue[startIndex]))
                    {
                        startIndex++;
                    }

                    stringValue = stringValue.Substring(0, 19) + stringValue.Substring(startIndex);
                }

                DateTimeOffset result;
                if (DateTimeOffset.TryParseExact(
                        stringValue,
                        "yyyy-MM-ddTHH:mm:sszzz",
                        CultureInfo.InvariantCulture.DateTimeFormat,
                        DateTimeStyles.None,
                        out result))
                {
                    return(result);
                }

                if (DateTimeOffset.TryParseExact(
                        stringValue,
                        "yyyy-MM-ddTHH:mm:ssZ",
                        CultureInfo.InvariantCulture.DateTimeFormat,
                        DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
                        out result))
                {
                    return(result);
                }
            }

            // As a fallback use XmlConvert.ToDateTimeOffset (Note that Syndication API would actually fails if it got here).
            // This is what ATOM RFC specifies, the value should conform to xsd:dateTime, which is exactly what the below method parses.
            return(PlatformHelper.ConvertStringToDateTimeOffset(stringValue));
        }
        public void ConvertStringToDateTimeOffsetShouldThrowIfTimeZoneIsMissing()
        {
            string dateTimeOffsetStr = "2013-11-04T19:09:26";
            Action test = () => PlatformHelper.ConvertStringToDateTimeOffset(dateTimeOffsetStr);

            test.ShouldThrow <FormatException>(Strings.PlatformHelper_DateTimeOffsetMustContainTimeZone(dateTimeOffsetStr));
        }
        /// <summary>
        /// Converts the given JSON string value to the expected type as per OData conversion rules for JSON values.
        /// </summary>
        /// <param name="stringValue">String value to the converted.</param>
        /// <param name="targetType">Target type to which the string value needs to be converted.</param>
        /// <returns>Object which is in sync with the target type.</returns>
        private static object ConvertStringValue(string stringValue, Type targetType)
        {
            // COMPAT 53: Support for System.Data.Linq.Binary and System.Xml.Linq.XElement
            if (targetType == typeof(byte[]))
            {
                return(Convert.FromBase64String(stringValue));
            }

            if (targetType == typeof(Guid))
            {
                return(new Guid(stringValue));
            }

            // Convert.ChangeType does not support TimeSpan.
            if (targetType == typeof(TimeSpan))
            {
                return(EdmValueParser.ParseDuration(stringValue));
            }

            // Date
            if (targetType == typeof(Date))
            {
                return(PlatformHelper.ConvertStringToDate(stringValue));
            }

            // Time
            if (targetType == typeof(TimeOfDay))
            {
                return(PlatformHelper.ConvertStringToTimeOfDay(stringValue));
            }

            // DateTimeOffset needs to be read using the XML rules (as per the JSON Light spec).
            if (targetType == typeof(DateTimeOffset))
            {
                return(PlatformHelper.ConvertStringToDateTimeOffset(stringValue));
            }

            if (targetType == typeof(Double) || targetType == typeof(Single))
            {
                // Accept Infinity and -Infinity to preserve consistency
                if (stringValue == CultureInfo.InvariantCulture.NumberFormat.PositiveInfinitySymbol)
                {
                    stringValue = JsonValueUtils.ODataJsonPositiveInfinitySymbol;
                }
                else if (stringValue == CultureInfo.InvariantCulture.NumberFormat.NegativeInfinitySymbol)
                {
                    stringValue = JsonValueUtils.ODataJsonNegativeInfinitySymbol;
                }

                return(Convert.ChangeType(stringValue, targetType, JsonValueUtils.ODataNumberFormatInfo));
            }

            // For string types, we support conversion to all possible primitive types
            return(Convert.ChangeType(stringValue, targetType, CultureInfo.InvariantCulture));
        }
예제 #6
0
 internal static bool TryParseDateTimeOffset(string value, out DateTimeOffset?result)
 {
     try
     {
         result = PlatformHelper.ConvertStringToDateTimeOffset(value);
         return(true);
     }
     catch (FormatException)
     {
         result = null;
         return(false);
     }
 }
예제 #7
0
        /// <summary>
        /// Converts a string to a DateTimeOffset value.
        /// TODO:  There is same method in ODataLibrary in UriUtils
        /// Currently there are two ExpressionLexer classes, one in Server and one in ODataLibrary
        /// We need to get rid of this class in Server
        /// </summary>
        /// <param name="text">String text to convert.</param>
        /// <param name="targetValue">After invocation, converted value.</param>
        /// <returns>true if the value was converted; false otherwise.</returns>
        /// <remarks>Copy of WebConvert.TryKeyStringToDateTimeOffset.</remarks>
        internal static bool TryUriStringToDateTimeOffset(string text, out DateTimeOffset targetValue)
        {
            targetValue = default(DateTimeOffset);

            try
            {
                targetValue = PlatformHelper.ConvertStringToDateTimeOffset(text);
                return(true);
            }
            catch (FormatException)
            {
                return(false);
            }
        }
예제 #8
0
        /// <summary>
        /// Converts the given JSON string value to the expected type as per OData conversion rules for JSON values.
        /// </summary>
        /// <param name="stringValue">String value to the converted.</param>
        /// <param name="targetType">Target type to which the string value needs to be converted.</param>
        /// <param name="version">The version of the payload being read.</param>
        /// <returns>Object which is in sync with the target type.</returns>
        private static object ConvertStringValue(string stringValue, Type targetType, ODataVersion version)
        {
            if (targetType == typeof(byte[]))
            {
                return(Convert.FromBase64String(stringValue));
            }

            if (targetType == typeof(Guid))
            {
                return(new Guid(stringValue));
            }

            // Convert.ChangeType does not support TimeSpan.
            if (targetType == typeof(TimeSpan))
            {
                return(XmlConvert.ToTimeSpan(stringValue));
            }

            // Convert.ChangeType does not support DateTimeOffset.
            // Convert.ChangeType does support DateTime, and hence the ChangeType
            // call below should handle the DateTime case.
            if (targetType == typeof(DateTimeOffset))
            {
                return(PlatformHelper.ConvertStringToDateTimeOffset(stringValue));
            }

            // In Verbose JSON V3 the DateTime fomat is the ISO one, so we need to call XmlConvert instead of Convert
            // Convert doesn't read that value correctly (converts the result to Local kind always).
            if (targetType == typeof(DateTime) && version >= ODataVersion.V3)
            {
                try
                {
                    return(PlatformHelper.ConvertStringToDateTime(stringValue));
                }
                catch (FormatException)
                {
                    // If the XmlConvert fails to convert we need to try Convert.ChangeType on the value anyway
                    // so that we can still read values like MM/DD/YYYY which we supported just fine in V1/V2.
                }
            }

            // For string types, we support conversion to all possible primitive types
            return(Convert.ChangeType(stringValue, targetType, CultureInfo.InvariantCulture));
        }
예제 #9
0
 /// <summary>
 /// Create an instance of primitive type from a string representation
 /// </summary>
 /// <param name="text">The string representation</param>
 /// <returns>An instance of primitive type</returns>
 internal override object Parse(String text)
 {
     return(PlatformHelper.ConvertStringToDateTimeOffset(text));
 }
예제 #10
0
        internal static object ConvertStringToPrimitive(string text, IEdmPrimitiveTypeReference targetTypeReference)
        {
            Debug.Assert(text != null, "text != null");
            Debug.Assert(targetTypeReference != null, "targetTypeReference != null");

            try
            {
                EdmPrimitiveTypeKind primitiveKind = targetTypeReference.PrimitiveKind();

                switch (primitiveKind)
                {
                case EdmPrimitiveTypeKind.Binary:
                    return(Convert.FromBase64String(text));

                case EdmPrimitiveTypeKind.Boolean:
                    return(ConvertXmlBooleanValue(text));

                case EdmPrimitiveTypeKind.Byte:
                    return(XmlConvert.ToByte(text));

                case EdmPrimitiveTypeKind.DateTimeOffset:
                    return(PlatformHelper.ConvertStringToDateTimeOffset(text));

                case EdmPrimitiveTypeKind.Decimal:
                    return(XmlConvert.ToDecimal(text));

                case EdmPrimitiveTypeKind.Double:
                    return(XmlConvert.ToDouble(text));

                case EdmPrimitiveTypeKind.Guid:
                    return(new Guid(text));

                case EdmPrimitiveTypeKind.Int16:
                    return(XmlConvert.ToInt16(text));

                case EdmPrimitiveTypeKind.Int32:
                    return(XmlConvert.ToInt32(text));

                case EdmPrimitiveTypeKind.Int64:
                    return(XmlConvert.ToInt64(text));

                case EdmPrimitiveTypeKind.SByte:
                    return(XmlConvert.ToSByte(text));

                case EdmPrimitiveTypeKind.Single:
                    return(XmlConvert.ToSingle(text));

                case EdmPrimitiveTypeKind.String:
                    return(text);

                case EdmPrimitiveTypeKind.Duration:
                    return(EdmValueParser.ParseDuration(text));

                case EdmPrimitiveTypeKind.Date:
                    return(PlatformHelper.ConvertStringToDate(text));

                case EdmPrimitiveTypeKind.TimeOfDay:
                    return(PlatformHelper.ConvertStringToTimeOfDay(text));

                case EdmPrimitiveTypeKind.Stream:
                case EdmPrimitiveTypeKind.None:
                case EdmPrimitiveTypeKind.Geography:
                case EdmPrimitiveTypeKind.GeographyCollection:
                case EdmPrimitiveTypeKind.GeographyPoint:
                case EdmPrimitiveTypeKind.GeographyLineString:
                case EdmPrimitiveTypeKind.GeographyPolygon:
                case EdmPrimitiveTypeKind.GeometryCollection:
                case EdmPrimitiveTypeKind.GeographyMultiPolygon:
                case EdmPrimitiveTypeKind.GeographyMultiLineString:
                case EdmPrimitiveTypeKind.GeographyMultiPoint:
                case EdmPrimitiveTypeKind.Geometry:
                case EdmPrimitiveTypeKind.GeometryPoint:
                case EdmPrimitiveTypeKind.GeometryLineString:
                case EdmPrimitiveTypeKind.GeometryPolygon:
                case EdmPrimitiveTypeKind.GeometryMultiPolygon:
                case EdmPrimitiveTypeKind.GeometryMultiLineString:
                case EdmPrimitiveTypeKind.GeometryMultiPoint:
                default:
                    // Note that Astoria supports XElement and Binary as well, but they are serialized as string or byte[]
                    // and the metadata will actually talk about string and byte[] as well. Astoria will perform the conversion if necessary.
                    throw new ODataException(Strings.General_InternalError(InternalErrorCodes.AtomValueUtils_ConvertStringToPrimitive));
                }
            }
            catch (Exception e)
            {
                if (!ExceptionUtils.IsCatchableExceptionType(e))
                {
                    throw;
                }

                throw ReaderValidationUtils.GetPrimitiveTypeConversionException(targetTypeReference, e, text);
            }
        }
예제 #11
0
 public void ConvertStringToDateTimeOffsetShouldConvertDateTimeStringEndsWithLowerCaseZ()
 {
     PlatformHelper.ConvertStringToDateTimeOffset("2013-11-04T19:09:26z").Offset.Should().Be(new TimeSpan(0));
 }
예제 #12
0
 public void ConvertStringToDateTimeOffsetShouldConvertDateTimeStringWithMinusOffset()
 {
     PlatformHelper.ConvertStringToDateTimeOffset("2013-11-04T19:09:26-08:00").Offset.Should().Be(new TimeSpan(-8, 0, 0));
 }