2 using System.Collections.Generic;
4 using System.Reflection;
7 namespace System.Runtime.Serialization
9 internal partial class XmlFormatReaderGenerator
11 partial class CriticalHelper
13 internal XmlFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
15 return (XmlReaderDelegator xr, XmlObjectSerializerReadContext ctx, XmlDictionaryString [] memberNames, XmlDictionaryString [] memberNamespaces) => new XmlFormatReaderInterpreter (classContract).ReadFromXml (xr, ctx, memberNames, memberNamespaces);
18 internal XmlFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
20 return (XmlReaderDelegator xr, XmlObjectSerializerReadContext ctx, XmlDictionaryString inm, XmlDictionaryString ins, CollectionDataContract cc) => new XmlFormatReaderInterpreter (collectionContract, false).ReadCollectionFromXml (xr, ctx, inm, ins, cc);
23 internal XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
25 return (XmlReaderDelegator xr, XmlObjectSerializerReadContext ctx, XmlDictionaryString inm, XmlDictionaryString ins, CollectionDataContract cc) => new XmlFormatReaderInterpreter (collectionContract, true).ReadGetOnlyCollectionFromXml (xr, ctx, inm, ins, cc);
30 class XmlFormatReaderInterpreter
32 public XmlFormatReaderInterpreter (ClassDataContract classContract)
34 this.classContract = classContract;
37 public XmlFormatReaderInterpreter (CollectionDataContract collectionContract, bool isGetOnly)
39 this.collectionContract = collectionContract;
40 this.is_get_only_collection = isGetOnly;
43 bool is_get_only_collection;
45 ClassDataContract classContract;
47 CollectionDataContract collectionContract;
51 XmlReaderDelegator xmlReader;
52 XmlObjectSerializerReadContext context;
54 XmlDictionaryString [] memberNames = null;
55 XmlDictionaryString [] memberNamespaces = null;
56 XmlDictionaryString itemName = null;
57 XmlDictionaryString itemNamespace = null;
59 public object ReadFromXml (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces)
62 this.xmlReader = xmlReader;
63 this.context = context;
64 this.memberNames = memberNames;
65 this.memberNamespaces = memberNamespaces;
67 //DemandSerializationFormatterPermission(classContract);
68 //DemandMemberAccessPermission(memberAccessFlag);
69 CreateObject (classContract);
71 context.AddNewObject (objectLocal);
72 InvokeOnDeserializing (classContract);
74 string objectId = null;
76 if (HasFactoryMethod (classContract))
77 objectId = context.GetObjectId ();
78 if (classContract.IsISerializable)
79 ReadISerializable (classContract);
81 ReadClass (classContract);
82 bool isFactoryType = InvokeFactoryMethod (classContract, objectId);
83 if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom (classContract.UnderlyingType))
84 ((IDeserializationCallback) objectLocal).OnDeserialization (null);
85 InvokeOnDeserialized(classContract);
86 if (objectId == null || !isFactoryType) {
88 // Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
89 // DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
90 // on DateTimeOffset; which does not work in partial trust.
92 if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
93 objectLocal = DateTimeOffsetAdapter.GetDateTimeOffset ((DateTimeOffsetAdapter) objectLocal);
94 // else - do we have to call CodeInterpreter.ConvertValue()? I guess not...
99 public object ReadCollectionFromXml (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract)
101 #region GenerateCollectionReaderHelper
103 this.xmlReader = xmlReader;
104 this.context = context;
105 this.itemName = itemName;
106 this.itemNamespace = itemNamespace;
108 this.collectionContract = collectionContract;
112 ReadCollection (collectionContract);
117 public void ReadGetOnlyCollectionFromXml (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context, XmlDictionaryString itemName, XmlDictionaryString itemNamespace, CollectionDataContract collectionContract)
119 #region GenerateCollectionReaderHelper
121 this.xmlReader = xmlReader;
122 this.context = context;
123 this.itemName = itemName;
124 this.itemNamespace = itemNamespace;
126 this.collectionContract = collectionContract;
130 ReadGetOnlyCollection (collectionContract);
133 void CreateObject (ClassDataContract classContract)
135 Type type = objectType = classContract.UnderlyingType;
136 if (type.IsValueType && !classContract.IsNonAttributedType)
137 type = Globals.TypeOfValueType;
139 if (classContract.UnderlyingType == Globals.TypeOfDBNull)
140 objectLocal = DBNull.Value;
141 else if (classContract.IsNonAttributedType) {
142 if (type.IsValueType)
143 objectLocal = FormatterServices.GetUninitializedObject (type);
145 objectLocal = classContract.GetNonAttributedTypeConstructor ().Invoke (new object [0]);
148 objectLocal = CodeInterpreter.ConvertValue (XmlFormatReaderGenerator.UnsafeGetUninitializedObject (DataContract.GetIdForInitialization (classContract)), Globals.TypeOfObject, type);
151 void InvokeOnDeserializing (ClassDataContract classContract)
153 if (classContract.BaseContract != null)
154 InvokeOnDeserializing (classContract.BaseContract);
155 if (classContract.OnDeserializing != null)
156 classContract.OnDeserializing.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
159 void InvokeOnDeserialized (ClassDataContract classContract)
161 if (classContract.BaseContract != null)
162 InvokeOnDeserialized (classContract.BaseContract);
163 if (classContract.OnDeserialized != null)
164 classContract.OnDeserialized.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
167 bool HasFactoryMethod (ClassDataContract classContract)
169 return Globals.TypeOfIObjectReference.IsAssignableFrom (classContract.UnderlyingType);
172 bool InvokeFactoryMethod (ClassDataContract classContract, string objectId)
174 if (HasFactoryMethod (classContract)) {
175 objectLocal = CodeInterpreter.ConvertValue (context.GetRealObject ((IObjectReference) objectLocal, objectId), Globals.TypeOfObject, classContract.UnderlyingType);
181 void ReadISerializable (ClassDataContract classContract)
183 ConstructorInfo ctor = classContract.GetISerializableConstructor ();
184 var info = context.ReadSerializationInfo (xmlReader, classContract.UnderlyingType);
185 ctor.Invoke (objectLocal, new object [] {info, context.GetStreamingContext ()});
188 void ReadClass (ClassDataContract classContract)
190 if (classContract.HasExtensionData) {
191 ExtensionDataObject extensionData = new ExtensionDataObject ();
192 ReadMembers (classContract, extensionData);
193 ClassDataContract currentContract = classContract;
194 while (currentContract != null) {
195 MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
196 if (extensionDataSetMethod != null)
197 extensionDataSetMethod.Invoke (objectLocal, new object [] {extensionData});
198 currentContract = currentContract.BaseContract;
202 ReadMembers (classContract, null);
205 void ReadMembers (ClassDataContract classContract, ExtensionDataObject extensionData)
207 int memberCount = classContract.MemberNames.Length;
208 context.IncrementItemCount (memberCount);
210 int memberIndex = -1;
212 int firstRequiredMember;
213 bool[] requiredMembers = GetRequiredMembers (classContract, out firstRequiredMember);
214 bool hasRequiredMembers = (firstRequiredMember < memberCount);
215 int requiredIndex = hasRequiredMembers ? firstRequiredMember : memberCount;
217 while (XmlObjectSerializerReadContext.MoveToNextElement (xmlReader)) {
218 int idx; // used as in "switch (idx)" in the original source.
219 if (hasRequiredMembers)
220 idx = context.GetMemberIndexWithRequiredMembers (xmlReader, memberNames, memberNamespaces, memberIndex, (int) requiredIndex, extensionData);
222 idx = context.GetMemberIndex (xmlReader, memberNames, memberNamespaces, memberIndex, extensionData);
225 ReadMembers (idx, classContract, requiredMembers, ref memberIndex, ref requiredIndex);
228 if (hasRequiredMembers)
230 if (requiredIndex < memberCount)
231 XmlObjectSerializerReadContext.ThrowRequiredMemberMissingException (xmlReader, memberIndex, requiredIndex, memberNames);
235 int ReadMembers (int index, ClassDataContract classContract, bool [] requiredMembers, ref int memberIndex, ref int requiredIndex)
237 int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers (index, classContract.BaseContract, requiredMembers,
238 ref memberIndex, ref requiredIndex);
240 if (memberCount <= index && index < memberCount + classContract.Members.Count) {
241 DataMember dataMember = classContract.Members [index - memberCount];
242 Type memberType = dataMember.MemberType;
243 if (dataMember.IsRequired) {
244 int nextRequiredIndex = index + 1;
245 for (; nextRequiredIndex < requiredMembers.Length; nextRequiredIndex++)
246 if (requiredMembers [nextRequiredIndex])
248 requiredIndex = nextRequiredIndex;
251 if (dataMember.IsGetOnlyCollection) {
252 var value = CodeInterpreter.GetMember (dataMember.MemberInfo, objectLocal);
253 context.StoreCollectionMemberInfo (value);
254 ReadValue (memberType, dataMember.Name, classContract.StableName.Namespace);
256 var value = ReadValue (memberType, dataMember.Name, classContract.StableName.Namespace);
257 CodeInterpreter.SetMember (dataMember.MemberInfo, objectLocal, value);
261 return memberCount + classContract.Members.Count;
264 bool[] GetRequiredMembers (ClassDataContract contract, out int firstRequiredMember)
266 int memberCount = contract.MemberNames.Length;
267 bool [] requiredMembers = new bool [memberCount];
268 GetRequiredMembers (contract, requiredMembers);
269 for (firstRequiredMember = 0; firstRequiredMember < memberCount; firstRequiredMember++)
270 if (requiredMembers [firstRequiredMember])
272 return requiredMembers;
275 int GetRequiredMembers (ClassDataContract contract, bool[] requiredMembers)
277 int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers (contract.BaseContract, requiredMembers);
278 List<DataMember> members = contract.Members;
279 for (int i = 0; i < members.Count; i++, memberCount++)
280 requiredMembers [memberCount] = members [i].IsRequired;
284 object ReadValue (Type type, string name, string ns)
286 var valueType = type;
288 bool shouldAssignNullableValue = false;
290 while (type.IsGenericType && type.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
292 type = type.GetGenericArguments () [0];
295 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (type);
296 if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType) {
297 context.ReadAttributes (xmlReader);
298 string objectId = context.ReadIfNullOrRef (xmlReader, type, DataContract.IsTypeSerializable (type));
300 if (objectId == Globals.NullObjectId) {
303 value = Activator.CreateInstance (valueType);
304 else if (type.IsValueType)
305 throw new SerializationException (SR.GetString (SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName (type)));
308 } else if (objectId == string.Empty) {
311 // Compare against Globals.NewObjectId, which is set to string.Empty
313 objectId = context.GetObjectId ();
315 if (type.IsValueType) {
316 if (!string.IsNullOrEmpty (objectId))
317 throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
319 object innerValueRead = null;
321 shouldAssignNullableValue = true;
323 if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
324 value = primitiveContract.XmlFormatReaderMethod.Invoke (xmlReader, new object [0]);
325 if (!type.IsValueType)
326 context.AddNewObject (value);
329 value = InternalDeserialize (type, name, ns);
332 if (type.IsValueType)
333 throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName (type)));
335 value = CodeInterpreter.ConvertValue (context.GetExistingObject (objectId, type, name, ns), Globals.TypeOfObject, type);
338 if (shouldAssignNullableValue) {
339 if (objectId != Globals.NullObjectId)
340 value = WrapNullableObject (type, value, valueType, nullables);
344 value = InternalDeserialize (type, name, ns);
349 object InternalDeserialize (Type type, string name, string ns)
351 Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
352 var obj = context.InternalDeserialize (xmlReader, DataContract.GetId (declaredType.TypeHandle), declaredType.TypeHandle, name, ns);
355 // wow, there is no way to convert void* to object in strongly typed way...
356 return XmlFormatGeneratorStatics.UnboxPointer.Invoke (null, new object [] {obj});
358 return CodeInterpreter.ConvertValue (obj, Globals.TypeOfObject, type);
361 object WrapNullableObject (Type innerType, object innerValue, Type outerType, int nullables)
363 var outerValue = innerValue;
364 for (int i = 1; i < nullables; i++) {
365 Type type = Globals.TypeOfNullable.MakeGenericType (innerType);
366 outerValue = Activator.CreateInstance (type, new object[] { outerValue });
369 return Activator.CreateInstance (outerType, new object[] { outerValue });
373 void ReadCollection (CollectionDataContract collectionContract)
375 Type type = collectionContract.UnderlyingType;
376 Type itemType = collectionContract.ItemType;
377 bool isArray = (collectionContract.Kind == CollectionKind.Array);
379 ConstructorInfo constructor = collectionContract.Constructor;
381 if (type.IsInterface) {
382 switch (collectionContract.Kind) {
383 case CollectionKind.GenericDictionary:
384 type = Globals.TypeOfDictionaryGeneric.MakeGenericType (itemType.GetGenericArguments ());
385 constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
387 case CollectionKind.Dictionary:
388 type = Globals.TypeOfHashtable;
389 constructor = XmlFormatGeneratorStatics.HashtableCtor;
391 case CollectionKind.Collection:
392 case CollectionKind.GenericCollection:
393 case CollectionKind.Enumerable:
394 case CollectionKind.GenericEnumerable:
395 case CollectionKind.List:
396 case CollectionKind.GenericList:
397 type = itemType.MakeArrayType ();
402 string itemName = collectionContract.ItemName;
403 string itemNs = collectionContract.StableName.Namespace;
406 if (type.IsValueType)
407 // FIXME: this is not what the original code does.
408 objectLocal = FormatterServices.GetUninitializedObject (type);
410 objectLocal = constructor.Invoke (new object [0]);
411 context.AddNewObject (objectLocal);
415 int size = context.GetArraySize ();
417 string objectId = context.GetObjectId ();
419 bool canReadPrimitiveArray = false, readResult = false;
420 if (isArray && TryReadPrimitiveArray (type, itemType, size, out readResult))
421 canReadPrimitiveArray = true;
426 object growingCollection = null;
428 growingCollection = Array.CreateInstance (itemType, 32);
431 // FIXME: I cannot find i++ part, but without that it won't work as expected.
432 for (; i < int.MaxValue; i++) {
433 if (IsStartElement (this.itemName, this.itemNamespace)) {
434 context.IncrementItemCount (1);
435 object value = ReadCollectionItem (collectionContract, itemType, itemName, itemNs);
437 MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod (itemType);
438 growingCollection = ensureArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
439 ((Array) growingCollection).SetValue (value, i);
441 StoreCollectionValue (objectLocal, itemType, value, collectionContract);
444 else if (IsEndElement ())
447 HandleUnexpectedItemInCollection (ref i);
451 MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod (itemType);
452 objectLocal = trimArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
453 context.AddNewObjectWithId (objectId, objectLocal);
456 context.IncrementItemCount (size);
458 objectLocal = Array.CreateInstance (itemType, size);
459 context.AddNewObject (objectLocal);
461 // FIXME: I cannot find j++ part, but without that it won't work as expected.
462 for (int j = 0; j < size; j++) {
463 if (IsStartElement (this.itemName, this.itemNamespace)) {
464 var itemValue = ReadCollectionItem (collectionContract, itemType, itemName, itemNs);
466 ((Array) objectLocal).SetValue (itemValue, j);
468 StoreCollectionValue (objectLocal, itemType, itemValue, collectionContract);
471 HandleUnexpectedItemInCollection (ref j);
473 context.CheckEndOfArray (xmlReader, size, this.itemName, this.itemNamespace);
476 if (canReadPrimitiveArray)
477 context.AddNewObjectWithId (objectId, objectLocal);
480 void ReadGetOnlyCollection (CollectionDataContract collectionContract)
482 Type type = collectionContract.UnderlyingType;
483 Type itemType = collectionContract.ItemType;
484 bool isArray = (collectionContract.Kind == CollectionKind.Array);
485 string itemName = collectionContract.ItemName;
486 string itemNs = collectionContract.StableName.Namespace;
488 objectLocal = context.GetCollectionMember ();
490 //check that items are actually going to be deserialized into the collection
491 if (IsStartElement (this.itemName, this.itemNamespace)) {
492 if (objectLocal == null)
493 XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
497 size = ((Array) objectLocal).Length;
498 context.AddNewObject (objectLocal);
499 for (int i = 0; i < int.MaxValue;) {
500 if (IsStartElement (this.itemName, this.itemNamespace)) {
501 context.IncrementItemCount (1);
502 var value = ReadCollectionItem (collectionContract, itemType, itemName, itemNs);
505 XmlObjectSerializerReadContext.ThrowArrayExceededSizeException (size, type);
507 ((Array) objectLocal).SetValue (value, i);
509 StoreCollectionValue (objectLocal, itemType, value, collectionContract);
512 else if (IsEndElement())
515 HandleUnexpectedItemInCollection (ref i);
517 context.CheckEndOfArray (xmlReader, size, this.itemName, this.itemNamespace);
522 bool TryReadPrimitiveArray (Type type, Type itemType, int size, out bool readResult)
525 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (itemType);
526 if (primitiveContract == null)
529 string readArrayMethod = null;
530 switch (Type.GetTypeCode (itemType))
532 case TypeCode.Boolean:
533 readArrayMethod = "TryReadBooleanArray";
535 case TypeCode.DateTime:
536 readArrayMethod = "TryReadDateTimeArray";
538 case TypeCode.Decimal:
539 readArrayMethod = "TryReadDecimalArray";
542 readArrayMethod = "TryReadInt32Array";
545 readArrayMethod = "TryReadInt64Array";
547 case TypeCode.Single:
548 readArrayMethod = "TryReadSingleArray";
550 case TypeCode.Double:
551 readArrayMethod = "TryReadDoubleArray";
556 if (readArrayMethod != null) {
557 var mi = typeof (XmlReaderDelegator).GetMethod (readArrayMethod, Globals.ScanAllMembers);
558 var args = new object [] {context, itemName, itemNamespace, size, objectLocal};
559 readResult = (bool) mi.Invoke (xmlReader, args);
560 objectLocal = args.Last ();
566 object ReadCollectionItem (CollectionDataContract collectionContract, Type itemType, string itemName, string itemNs)
568 if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) {
569 context.ResetAttributes ();
570 return CodeInterpreter.ConvertValue (collectionContract.ItemContract.ReadXmlValue (xmlReader, context), Globals.TypeOfObject, itemType);
573 return ReadValue (itemType, itemName, itemNs);
576 void StoreCollectionValue (object collection, Type valueType, object value, CollectionDataContract collectionContract)
578 if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) {
579 ClassDataContract keyValuePairContract = DataContract.GetDataContract (valueType) as ClassDataContract;
580 if (keyValuePairContract == null)
581 Fx.Assert ("Failed to create contract for KeyValuePair type");
582 DataMember keyMember = keyValuePairContract.Members [0];
583 DataMember valueMember = keyValuePairContract.Members [1];
584 object pkey = CodeInterpreter.GetMember (keyMember.MemberInfo, value);
585 object pvalue = CodeInterpreter.GetMember (valueMember.MemberInfo, value);
587 collectionContract.AddMethod.Invoke (collection, new object [] {pkey, pvalue});
590 collectionContract.AddMethod.Invoke (collection, new object [] {value});
593 void HandleUnexpectedItemInCollection (ref int iterator)
595 if (IsStartElement ()) {
596 context.SkipUnknownElement (xmlReader);
600 throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (XmlNodeType.Element, xmlReader);
603 bool IsStartElement(XmlDictionaryString name, XmlDictionaryString ns)
605 return xmlReader.IsStartElement (name, ns);
608 bool IsStartElement()
610 return xmlReader.IsStartElement ();
615 return xmlReader.NodeType == XmlNodeType.EndElement;