/// <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)); }
/// <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); } }
/// <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)); }
internal static bool TryParseDateTimeOffset(string value, out DateTimeOffset?result) { try { result = PlatformHelper.ConvertStringToDateTimeOffset(value); return(true); } catch (FormatException) { result = null; return(false); } }
/// <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); } }
/// <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)); }
/// <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)); }
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); } }
public void ConvertStringToDateTimeOffsetShouldConvertDateTimeStringEndsWithLowerCaseZ() { PlatformHelper.ConvertStringToDateTimeOffset("2013-11-04T19:09:26z").Offset.Should().Be(new TimeSpan(0)); }
public void ConvertStringToDateTimeOffsetShouldConvertDateTimeStringWithMinusOffset() { PlatformHelper.ConvertStringToDateTimeOffset("2013-11-04T19:09:26-08:00").Offset.Should().Be(new TimeSpan(-8, 0, 0)); }