Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,10 @@ internal static bool TryCreateBuiltInDataContract(Type type, [NotNullWhen(true)]
dataContract = new TimeSpanDataContract();
else if (type == typeof(Guid))
dataContract = new GuidDataContract();
else if (type == typeof(DateOnly))
dataContract = new DateOnlyDataContract();
else if (type == typeof(TimeOnly))
dataContract = new TimeOnlyDataContract();
else if (type == typeof(Enum) || type == typeof(ValueType))
{
dataContract = new SpecialTypeDataContract(type, DictionaryGlobals.ObjectLocalName, DictionaryGlobals.SchemaNamespace);
Expand Down Expand Up @@ -864,6 +868,10 @@ internal static bool TryCreateBuiltInDataContract(string name, string ns, [NotNu
dataContract = new GuidDataContract();
else if (DictionaryGlobals.CharLocalName.Value == name)
dataContract = new CharDataContract();
else if (DictionaryGlobals.DateOnlyLocalName.Value == name)
dataContract = new DateOnlyDataContract();
else if (DictionaryGlobals.TimeOnlyLocalName.Value == name)
dataContract = new TimeOnlyDataContract();
else if ("ArrayOfanyType" == name)
dataContract = new CollectionDataContract(typeof(Array));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,7 @@ internal static class DictionaryGlobals

// 60
public static readonly XmlDictionaryString AsmxTypesNamespace = s_dictionary.Add("http://microsoft.com/wsdl/types/");
public static readonly XmlDictionaryString DateOnlyLocalName = s_dictionary.Add("dateOnly");
public static readonly XmlDictionaryString TimeOnlyLocalName = s_dictionary.Add("timeOnly");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ internal static partial class Globals
internal static Type TypeOfGuid => field ??= typeof(Guid);
internal static Type TypeOfDateTimeOffset => field ??= typeof(DateTimeOffset);
internal static Type TypeOfDateTimeOffsetAdapter => field ??= typeof(DateTimeOffsetAdapter);
internal static Type TypeOfDateOnly => field ??= typeof(DateOnly);
internal static Type TypeOfTimeOnly => field ??= typeof(TimeOnly);
internal static Type TypeOfMemoryStream => field ??= typeof(MemoryStream);
internal static Type TypeOfMemoryStreamAdapter => field ??= typeof(MemoryStreamAdapter);
internal static Type TypeOfUri => field ??= typeof(Uri);
Expand Down Expand Up @@ -264,6 +266,16 @@ internal static Type TypeOfHashtable
<xs:attribute name='FactoryType' type='xs:QName' />
<xs:attribute name='Id' type='xs:ID' />
<xs:attribute name='Ref' type='xs:IDREF' />
<xs:simpleType name='dateOnly'>
<xs:restriction base='xs:date'>
<xs:pattern value='([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])' />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='timeOnly'>
<xs:restriction base='xs:time'>
<xs:pattern value='([01][0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9])(\.[0-9]{1,7})?)?' />
</xs:restriction>
</xs:simpleType>
</xs:schema>
";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,78 @@ internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj
}
}

internal sealed class DateOnlyDataContract : PrimitiveDataContract
{
public DateOnlyDataContract() : this(DictionaryGlobals.DateOnlyLocalName, DictionaryGlobals.SerializationNamespace)
{
}

internal DateOnlyDataContract(XmlDictionaryString name, XmlDictionaryString ns) : base(typeof(DateOnly), name, ns)
{
}

internal override string WriteMethodName => "WriteDateOnly";
internal override string ReadMethodName => "ReadElementContentAsDateOnly";

[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context)
{
writer.WriteDateOnly((DateOnly)obj);
}

[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context)
{
return (context == null) ? reader.ReadElementContentAsDateOnly()
: HandleReadValue(reader.ReadElementContentAsDateOnly(), context);
}

[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns)
{
xmlWriter.WriteDateOnly((DateOnly)obj!, name, ns);
}
}

internal sealed class TimeOnlyDataContract : PrimitiveDataContract
{
public TimeOnlyDataContract() : this(DictionaryGlobals.TimeOnlyLocalName, DictionaryGlobals.SerializationNamespace)
{
}

internal TimeOnlyDataContract(XmlDictionaryString name, XmlDictionaryString ns) : base(typeof(TimeOnly), name, ns)
{
}

internal override string WriteMethodName => "WriteTimeOnly";
internal override string ReadMethodName => "ReadElementContentAsTimeOnly";

[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal override void WriteXmlValue(XmlWriterDelegator writer, object obj, XmlObjectSerializerWriteContext? context)
{
writer.WriteTimeOnly((TimeOnly)obj);
}

[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal override object? ReadXmlValue(XmlReaderDelegator reader, XmlObjectSerializerReadContext? context)
{
return (context == null) ? reader.ReadElementContentAsTimeOnly()
: HandleReadValue(reader.ReadElementContentAsTimeOnly(), context);
}

[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, XmlObjectSerializerWriteContext context, XmlDictionaryString name, XmlDictionaryString? ns)
{
xmlWriter.WriteTimeOnly((TimeOnly)obj!, name, ns);
}
}

internal class StringDataContract : PrimitiveDataContract
{
public StringDataContract() : this(DictionaryGlobals.StringLocalName, DictionaryGlobals.SchemaNamespace)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,86 @@ internal virtual DateTime ReadContentAsDateTime()
return reader.ReadContentAsDateTime();
}

internal virtual DateOnly ReadElementContentAsDateOnly()
{
if (isEndOfEmptyElement)
ThrowNotAtElement();
string s = reader.ReadElementContentAsString();
try
{
return ParseDateOnly(s);
}
catch (Exception ex) when (ex is FormatException || ex is ArgumentException)
{
ThrowConversionException(s, nameof(DateOnly));
throw; // unreachable
}
}

internal virtual DateOnly ReadContentAsDateOnly()
{
if (isEndOfEmptyElement)
ThrowConversionException(string.Empty, nameof(DateOnly));
string s = reader.ReadContentAsString();
try
{
return ParseDateOnly(s);
}
catch (Exception ex) when (ex is FormatException || ex is ArgumentException)
{
ThrowConversionException(s, nameof(DateOnly));
throw; // unreachable
}
}

internal virtual TimeOnly ReadElementContentAsTimeOnly()
{
if (isEndOfEmptyElement)
ThrowNotAtElement();

string s = reader.ReadElementContentAsString();

try
{
var dto = XmlConvert.ToDateTimeOffset(s);
return TimeOnly.FromTimeSpan(dto.TimeOfDay);
}
catch (Exception ex) when (ex is FormatException || ex is ArgumentException)
{
ThrowConversionException(s, nameof(TimeOnly));
throw; // unreachable
}
}

internal virtual TimeOnly ReadContentAsTimeOnly()
{
if (isEndOfEmptyElement)
ThrowConversionException(string.Empty, nameof(TimeOnly));

string s = reader.ReadContentAsString();
try
{
var dto = XmlConvert.ToDateTimeOffset(s);
return TimeOnly.FromTimeSpan(dto.TimeOfDay);
}
catch (Exception ex) when (ex is FormatException || ex is ArgumentException)
{
ThrowConversionException(s, nameof(TimeOnly));
throw; // unreachable
}
}

private static DateOnly ParseDateOnly(string s)
{
return DateOnly.ParseExact(s, "yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite);
}

private static TimeOnly ParseTimeOnly(string s)
{
// Strictly parse the expected TimeOnly format. No timezone/offset allowed.
return TimeOnly.ParseExact(s, "HH:mm:ss.FFFFFFF", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite);
}

internal int ReadElementContentAsInt()
{
if (isEndOfEmptyElement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ internal void WriteAnyType(object value, Type valueType)
WriteUri((Uri)value);
else if (valueType == Globals.TypeOfXmlQualifiedName)
WriteQName((XmlQualifiedName)value);
else if (valueType == Globals.TypeOfDateOnly)
WriteDateOnly((DateOnly)value);
else if (valueType == Globals.TypeOfTimeOnly)
WriteTimeOnly((TimeOnly)value);
else
handled = false;
break;
Expand Down Expand Up @@ -426,6 +430,10 @@ internal void WriteExtensionData(IDataNode dataNode)
WriteUri(((DataNode<Uri>)dataNode).GetValue());
else if (valueType == Globals.TypeOfXmlQualifiedName)
WriteQName(((DataNode<XmlQualifiedName>)dataNode).GetValue());
else if (valueType == Globals.TypeOfDateOnly)
WriteDateOnly(((DataNode<DateOnly>)dataNode).GetValue());
else if (valueType == Globals.TypeOfTimeOnly)
WriteTimeOnly(((DataNode<TimeOnly>)dataNode).GetValue());
else
handled = false;
break;
Expand Down Expand Up @@ -465,6 +473,30 @@ internal void WriteDateTime(DateTime value, XmlDictionaryString name, XmlDiction
WriteEndElementPrimitive();
}

// DateOnly / TimeOnly
internal virtual void WriteDateOnly(DateOnly value)
{
writer.WriteString(value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
}
internal void WriteDateOnly(DateOnly value, XmlDictionaryString name, XmlDictionaryString? ns)
{
WriteStartElementPrimitive(name, ns);
WriteDateOnly(value);
WriteEndElementPrimitive();
}
internal virtual void WriteTimeOnly(TimeOnly value)
{
// Use optional fractional second digits (F) so trailing zeros and the '.' are omitted automatically.
// "f" forces zeros; "F" suppresses them. "HH:mm:ss.FFFFFFF" yields minimal length representation.
writer.WriteString(value.ToString("HH:mm:ss.FFFFFFF", CultureInfo.InvariantCulture));
}
internal void WriteTimeOnly(TimeOnly value, XmlDictionaryString name, XmlDictionaryString? ns)
{
WriteStartElementPrimitive(name, ns);
WriteTimeOnly(value);
WriteEndElementPrimitive();
}

internal virtual void WriteDecimal(decimal value)
{
writer.WriteValue(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ public static bool IgnoreKindInUtcTimeSerialization
}
}

private static int s_allowXsdTimeToTimeOnlyWithOffsetLoss;
public static bool AllowXsdTimeToTimeOnlyWithOffsetLoss
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return SwitchesHelpers.GetCachedSwitchValue("Switch.System.Xml.AllowXsdTimeToTimeOnlyWithOffsetLoss", ref s_allowXsdTimeToTimeOnlyWithOffsetLoss);
}
}

private static int s_limitXPathComplexity;
public static bool LimitXPathComplexity
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,34 @@ internal void Ldc(object o)
New(DateTimeOffset_ctor);
break;
}
else if (valueType == typeof(DateOnly))
{
ConstructorInfo DateOnly_ctor = typeof(DateOnly).GetConstructor(
CodeGenerator.InstanceBindingFlags,
null,
new Type[] { typeof(int), typeof(int), typeof(int) },
null
)!;
DateOnly dateOnly = (DateOnly)o;
Ldc(dateOnly.Year);
Ldc(dateOnly.Month);
Ldc(dateOnly.Day);
New(DateOnly_ctor);
break;
}
else if (valueType == typeof(TimeOnly))
{
ConstructorInfo TimeOnly_ctor = typeof(TimeOnly).GetConstructor(
CodeGenerator.InstanceBindingFlags,
null,
new Type[] { typeof(long) },
null
)!;
TimeOnly timeOnly = (TimeOnly)o;
Ldc(timeOnly.Ticks);
New(TimeOnly_ctor);
break;
}
else
{
throw new NotSupportedException(SR.Format(SR.UnknownConstantType, valueType.AssemblyQualifiedName));
Expand Down
Loading
Loading