11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33
4+ #if NETFRAMEWORK
5+ using System . CodeDom ;
6+ using System . Collections . ObjectModel ;
7+ #endif
8+ using System . Runtime . Serialization ;
49using System . Runtime . Serialization . Json ;
510
611namespace Microsoft . VisualStudio . TestPlatform . MSTest . TestAdapter . Helpers ;
@@ -13,6 +18,10 @@ internal static class DataSerializationHelper
1318 UseSimpleDictionaryFormat = true ,
1419 EmitTypeInformation = System . Runtime . Serialization . EmitTypeInformation . Always ,
1520 DateTimeFormat = new System . Runtime . Serialization . DateTimeFormat ( "O" , CultureInfo . InvariantCulture ) ,
21+ #if NETFRAMEWORK
22+ DataContractSurrogate = SerializationSurrogateProvider . Instance ,
23+ #endif
24+ KnownTypes = [ typeof ( SurrogatedDateOnly ) , typeof ( SurrogatedTimeOnly ) ] ,
1625 } ;
1726
1827 /// <summary>
@@ -47,6 +56,9 @@ internal static class DataSerializationHelper
4756 serializedData [ typeIndex ] = typeName ;
4857
4958 DataContractJsonSerializer serializer = GetSerializer ( type ) ;
59+ #if NET7_0_OR_GREATER
60+ serializer . SetSerializationSurrogateProvider ( SerializationSurrogateProvider . Instance ) ;
61+ #endif
5062
5163 using var memoryStream = new MemoryStream ( ) ;
5264 // This should be safe as long as our generator mentions
@@ -93,6 +105,9 @@ internal static class DataSerializationHelper
93105 }
94106
95107 DataContractJsonSerializer serializer = GetSerializer ( assemblyQualifiedName ) ;
108+ #if NET7_0_OR_GREATER
109+ serializer . SetSerializationSurrogateProvider ( SerializationSurrogateProvider . Instance ) ;
110+ #endif
96111
97112 byte [ ] serializedDataBytes = Encoding . UTF8 . GetBytes ( serializedValue ) ;
98113 using var memoryStream = new MemoryStream ( serializedDataBytes ) ;
@@ -104,6 +119,9 @@ internal static class DataSerializationHelper
104119 data [ i ] = serializer . ReadObject ( memoryStream ) ;
105120#pragma warning restore IL3050 // IL3050: Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as Native AOT
106121#pragma warning restore IL2026 // IL2026: Members attributed with RequiresUnreferencedCode may break when trimming
122+ // For some reason, we don't get SerializationSurrogateProvider.GetDeserializedObject to be called by .NET runtime.
123+ // So we manually call it.
124+ data [ i ] = SerializationSurrogateProvider . GetDeserializedObject ( data [ i ] ! ) ;
107125 }
108126
109127 return data ;
@@ -130,6 +148,93 @@ private static DataContractJsonSerializer GetSerializer(Type type)
130148#pragma warning disable IL3050 // IL3050: Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as Native AOT
131149#pragma warning disable IL2026 // IL2026: Members attributed with RequiresUnreferencedCode may break when trimming
132150 _ => new DataContractJsonSerializer ( type , SerializerSettings ) ) ;
151+
152+ [ DataContract ]
153+ private sealed class SurrogatedDateOnly
154+ {
155+ [ DataMember ]
156+ public int DayNumber { get ; set ; }
157+ }
158+
159+ [ DataContract ]
160+ private sealed class SurrogatedTimeOnly
161+ {
162+ [ DataMember ]
163+ public long Ticks { get ; set ; }
164+ }
165+
166+ private sealed class SerializationSurrogateProvider
167+ #if NETFRAMEWORK
168+ : IDataContractSurrogate
169+ #else
170+ : ISerializationSurrogateProvider
171+ #endif
172+ {
173+ public static SerializationSurrogateProvider Instance { get ; } = new ( ) ;
174+
175+ #if NETFRAMEWORK
176+ public object GetCustomDataToExport ( MemberInfo memberInfo , Type dataContractType ) => null ! ;
177+
178+ public object GetCustomDataToExport ( Type clrType , Type dataContractType ) => null ! ;
179+
180+ public void GetKnownCustomDataTypes ( Collection < Type > customDataTypes )
181+ {
182+ }
183+
184+ public Type GetReferencedTypeOnImport ( string typeName , string typeNamespace , object customData ) => null ! ;
185+
186+ public CodeTypeDeclaration ProcessImportedType ( CodeTypeDeclaration typeDeclaration , CodeCompileUnit compileUnit ) => typeDeclaration ;
187+ #endif
188+
189+ public object GetDeserializedObject ( object obj , Type targetType )
190+ => GetDeserializedObject ( obj ) ;
191+
192+ internal static object GetDeserializedObject ( object obj )
193+ {
194+ #if NET6_0_OR_GREATER
195+ if ( obj is SurrogatedDateOnly surrogatedDateOnly )
196+ {
197+ return DateOnly . FromDayNumber ( surrogatedDateOnly . DayNumber ) ;
198+ }
199+ else if ( obj is SurrogatedTimeOnly surrogatedTimeOnly )
200+ {
201+ return new TimeOnly ( surrogatedTimeOnly . Ticks ) ;
202+ }
203+ #endif
204+
205+ return obj ;
206+ }
207+
208+ public object GetObjectToSerialize ( object obj , Type targetType )
209+ => obj switch
210+ {
211+ #if NET6_0_OR_GREATER
212+ DateOnly dateOnly => new SurrogatedDateOnly ( ) { DayNumber = dateOnly . DayNumber } ,
213+ TimeOnly timeOnly => new SurrogatedTimeOnly ( ) { Ticks = timeOnly . Ticks } ,
214+ #endif
215+ _ => obj ,
216+ } ;
217+
218+ #if NETFRAMEWORK
219+ public Type GetDataContractType ( Type type )
220+ #else
221+ public Type GetSurrogateType ( Type type )
222+ #endif
223+ {
224+ #if NET6_0_OR_GREATER
225+ if ( type == typeof ( DateOnly ) )
226+ {
227+ return typeof ( SurrogatedDateOnly ) ;
228+ }
229+ else if ( type == typeof ( TimeOnly ) )
230+ {
231+ return typeof ( SurrogatedTimeOnly ) ;
232+ }
233+ #endif
234+
235+ return type ;
236+ }
237+ }
133238#pragma warning restore IL3050 // IL3050: Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as Native AOT
134239#pragma warning restore IL2026 // IL2026: Members attributed with RequiresUnreferencedCode may break when trimming
135240}
0 commit comments