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 = XmlFormatReaderGenerator.UnsafeGetUninitializedObject (DataContract.GetIdForInitialization (classContract));
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 = context.GetRealObject ((IObjectReference) objectLocal, objectId);
181 void ReadISerializable (ClassDataContract classContract)
183 ConstructorInfo ctor = classContract.GetISerializableConstructor ();
184 context.ReadSerializationInfo (xmlReader, classContract.UnderlyingType);
185 ctor.Invoke (objectLocal, new object [] {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 // FIXME: here I converted null as -1 but that may be wrong.
216 int requiredIndex = hasRequiredMembers ? firstRequiredMember : -1;
218 while (XmlObjectSerializerReadContext.MoveToNextElement (xmlReader)) {
219 int idx; // used as in "switch (idx)" in the original source.
220 if (hasRequiredMembers)
221 idx = context.GetMemberIndexWithRequiredMembers (xmlReader, memberNames, memberNamespaces, memberIndex, (int) requiredIndex, extensionData);
223 idx = context.GetMemberIndex (xmlReader, memberNames, memberNamespaces, memberIndex, extensionData);
226 ReadMembers (idx, classContract, requiredMembers, ref memberIndex, ref requiredIndex);
229 if (hasRequiredMembers)
231 if (requiredIndex < memberCount)
232 XmlObjectSerializerReadContext.ThrowRequiredMemberMissingException (xmlReader, memberIndex, requiredIndex, memberNames);
236 int ReadMembers (int index, ClassDataContract classContract, bool [] requiredMembers, ref int memberIndex, ref int requiredIndex)
238 int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers (index, classContract.BaseContract, requiredMembers,
239 ref memberIndex, ref requiredIndex);
241 if (memberCount <= index && index < memberCount + classContract.Members.Count) {
242 DataMember dataMember = classContract.Members [index];
243 Type memberType = dataMember.MemberType;
244 if (dataMember.IsRequired) {
245 int nextRequiredIndex = memberCount + 1;
246 for (; nextRequiredIndex < requiredMembers.Length; nextRequiredIndex++)
247 if (requiredMembers [nextRequiredIndex])
249 requiredIndex = nextRequiredIndex;
252 if (dataMember.IsGetOnlyCollection) {
253 var value = CodeInterpreter.GetMember (dataMember.MemberInfo, objectLocal);
254 context.StoreCollectionMemberInfo (value);
255 ReadValue (memberType, dataMember.Name, classContract.StableName.Namespace);
257 var value = ReadValue (memberType, dataMember.Name, classContract.StableName.Namespace);
258 CodeInterpreter.SetMember (dataMember.MemberInfo, objectLocal, value);
262 return memberCount + classContract.Members.Count;
265 bool[] GetRequiredMembers (ClassDataContract contract, out int firstRequiredMember)
267 int memberCount = contract.MemberNames.Length;
268 bool [] requiredMembers = new bool [memberCount];
269 GetRequiredMembers (contract, requiredMembers);
270 for (firstRequiredMember = 0; firstRequiredMember < memberCount; firstRequiredMember++)
271 if (requiredMembers [firstRequiredMember])
273 return requiredMembers;
276 int GetRequiredMembers (ClassDataContract contract, bool[] requiredMembers)
278 int memberCount = (contract.BaseContract == null) ? 0 : GetRequiredMembers (contract.BaseContract, requiredMembers);
279 List<DataMember> members = contract.Members;
280 for (int i = 0; i < members.Count; i++, memberCount++)
281 requiredMembers [memberCount] = members [i].IsRequired;
285 object ReadValue (Type type, string name, string ns)
287 var valueType = type;
289 bool shouldAssignNullableValue = false;
291 while (type.IsGenericType && type.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
293 type = type.GetGenericArguments () [0];
296 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (type);
297 if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType) {
298 context.ReadAttributes (xmlReader);
299 string objectId = context.ReadIfNullOrRef (xmlReader, type, DataContract.IsTypeSerializable (type));
301 if (objectId == Globals.NullObjectId) {
304 value = FormatterServices.GetUninitializedObject (valueType);
305 else if (type.IsValueType)
306 throw new SerializationException (SR.GetString (SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName (type)));
309 } else if (objectId == string.Empty) {
312 // Compare against Globals.NewObjectId, which is set to string.Empty
314 objectId = context.GetObjectId ();
316 if (type.IsValueType) {
317 if (!string.IsNullOrEmpty (objectId))
318 throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
320 object innerValueRead = null;
322 shouldAssignNullableValue = true;
324 if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
325 value = primitiveContract.XmlFormatReaderMethod.Invoke (xmlReader, new object [0]);
326 if (!type.IsValueType)
327 context.AddNewObject (value);
330 value = InternalDeserialize (value, type, name, ns);
333 if (type.IsValueType)
334 throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName (type)));
336 value = context.GetExistingObject (objectId, type, name, ns);
339 if (shouldAssignNullableValue) {
340 if (objectId != Globals.NullObjectId)
341 value = WrapNullableObject (type, value, valueType, nullables);
345 value = InternalDeserialize (value, type, name, ns);
350 object InternalDeserialize (object value, Type type, string name, string ns)
352 Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
353 var obj = context.InternalDeserialize (xmlReader, DataContract.GetId (declaredType.TypeHandle), declaredType.TypeHandle, name, ns);
356 // wow, there is no way to convert void* to object...
357 return XmlFormatGeneratorStatics.UnboxPointer.Invoke (null, new object [] {obj});
362 object WrapNullableObject (Type innerType, object innerValue, Type outerType, int nullables)
364 var outerValue = innerValue;
365 for (int i = 1; i < nullables; i++) {
366 Type type = Globals.TypeOfNullable.MakeGenericType (innerType);
367 outerValue = Activator.CreateInstance (type, new object[] { outerValue });
370 return Activator.CreateInstance (outerType, new object[] { outerValue });
374 void ReadCollection (CollectionDataContract collectionContract)
376 Type type = collectionContract.UnderlyingType;
377 Type itemType = collectionContract.ItemType;
378 bool isArray = (collectionContract.Kind == CollectionKind.Array);
380 ConstructorInfo constructor = collectionContract.Constructor;
382 if (type.IsInterface) {
383 switch (collectionContract.Kind) {
384 case CollectionKind.GenericDictionary:
385 type = Globals.TypeOfDictionaryGeneric.MakeGenericType (itemType.GetGenericArguments ());
386 constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
388 case CollectionKind.Dictionary:
389 type = Globals.TypeOfHashtable;
390 constructor = XmlFormatGeneratorStatics.HashtableCtor;
392 case CollectionKind.Collection:
393 case CollectionKind.GenericCollection:
394 case CollectionKind.Enumerable:
395 case CollectionKind.GenericEnumerable:
396 case CollectionKind.List:
397 case CollectionKind.GenericList:
398 type = itemType.MakeArrayType ();
403 string itemName = collectionContract.ItemName;
404 string itemNs = collectionContract.StableName.Namespace;
407 if (type.IsValueType)
408 // FIXME: this is not what the original code does.
409 objectLocal = FormatterServices.GetUninitializedObject (type);
411 objectLocal = constructor.Invoke (new object [0]);
412 context.AddNewObject (objectLocal);
416 int size = context.GetArraySize ();
418 string objectId = context.GetObjectId ();
420 bool canReadPrimitiveArray = false;
421 if (isArray && TryReadPrimitiveArray (type, itemType, size))
422 canReadPrimitiveArray = true;
424 if (!canReadPrimitiveArray) {
427 object growingCollection = null;
429 growingCollection = Array.CreateInstance (itemType, 32);
432 // FIXME: I cannot find i++ part, but without that it won't work as expected.
433 for (; i < int.MaxValue; i++) {
434 if (IsStartElement (this.itemName, this.itemNamespace)) {
435 context.IncrementItemCount (1);
436 object value = ReadCollectionItem (collectionContract, itemType, itemName, itemNs);
438 MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod (itemType);
439 growingCollection = ensureArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
440 ((Array) growingCollection).SetValue (value, i);
442 StoreCollectionValue (objectLocal, itemType, value, collectionContract);
445 else if (IsEndElement ())
448 HandleUnexpectedItemInCollection (ref i);
452 MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod (itemType);
453 objectLocal = trimArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
454 context.AddNewObjectWithId (objectId, objectLocal);
457 context.IncrementItemCount (size);
459 objectLocal = Array.CreateInstance (itemType, size);
460 context.AddNewObject (objectLocal);
462 // FIXME: I cannot find j++ part, but without that it won't work as expected.
463 for (int j = 0; j < size; j++) {
464 if (IsStartElement (this.itemName, this.itemNamespace)) {
465 var itemValue = ReadCollectionItem (collectionContract, itemType, itemName, itemNs);
467 ((Array) objectLocal).SetValue (itemValue, j);
469 StoreCollectionValue (objectLocal, itemType, itemValue, collectionContract);
472 HandleUnexpectedItemInCollection (ref j);
474 context.CheckEndOfArray (xmlReader, size, this.itemName, this.itemNamespace);
478 context.AddNewObjectWithId (objectId, objectLocal);
481 void ReadGetOnlyCollection (CollectionDataContract collectionContract)
483 Type type = collectionContract.UnderlyingType;
484 Type itemType = collectionContract.ItemType;
485 bool isArray = (collectionContract.Kind == CollectionKind.Array);
486 string itemName = collectionContract.ItemName;
487 string itemNs = collectionContract.StableName.Namespace;
489 objectLocal = context.GetCollectionMember ();
491 //check that items are actually going to be deserialized into the collection
492 if (IsStartElement (this.itemName, this.itemNamespace)) {
493 if (objectLocal == null)
494 XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
498 size = ((Array) objectLocal).Length;
499 context.AddNewObject (objectLocal);
500 for (int i = 0; i < int.MaxValue;) {
501 if (IsStartElement (this.itemName, this.itemNamespace)) {
502 context.IncrementItemCount (1);
503 var value = ReadCollectionItem (collectionContract, itemType, itemName, itemNs);
506 XmlObjectSerializerReadContext.ThrowArrayExceededSizeException (size, type);
508 ((Array) objectLocal).SetValue (value, i);
510 StoreCollectionValue (objectLocal, itemType, value, collectionContract);
513 else if (IsEndElement())
516 HandleUnexpectedItemInCollection (ref i);
518 context.CheckEndOfArray (xmlReader, size, this.itemName, this.itemNamespace);
523 bool TryReadPrimitiveArray (Type type, Type itemType, int size)
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 mi.Invoke (xmlReader, new object [] {context, itemName, itemNamespace, size, objectLocal});
564 object ReadCollectionItem (CollectionDataContract collectionContract, Type itemType, string itemName, string itemNs)
566 if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) {
567 context.ResetAttributes ();
568 return collectionContract.ItemContract.ReadXmlValue (xmlReader, context);
571 return ReadValue (itemType, itemName, itemNs);
574 void StoreCollectionValue (object collection, Type valueType, object value, CollectionDataContract collectionContract)
576 if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) {
577 ClassDataContract keyValuePairContract = DataContract.GetDataContract (valueType) as ClassDataContract;
578 if (keyValuePairContract == null)
579 Fx.Assert ("Failed to create contract for KeyValuePair type");
580 DataMember keyMember = keyValuePairContract.Members [0];
581 DataMember valueMember = keyValuePairContract.Members [1];
582 object pkey = CodeInterpreter.GetMember (keyMember.MemberInfo, value);
583 object pvalue = CodeInterpreter.GetMember (valueMember.MemberInfo, value);
585 collectionContract.AddMethod.Invoke (collection, new object [] {pkey, pvalue});
588 collectionContract.AddMethod.Invoke (collection, new object [] {value});
591 void HandleUnexpectedItemInCollection (ref int iterator)
593 if (IsStartElement ()) {
594 context.SkipUnknownElement (xmlReader);
598 throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (XmlNodeType.Element, xmlReader);
601 bool IsStartElement(XmlDictionaryString name, XmlDictionaryString ns)
603 return xmlReader.IsStartElement (name, ns);
606 bool IsStartElement()
608 return xmlReader.IsStartElement ();
613 return xmlReader.NodeType == XmlNodeType.EndElement;