2 using System.Collections;
3 using System.Collections.Generic;
5 using System.Reflection;
8 namespace System.Runtime.Serialization
10 internal partial class XmlFormatWriterGenerator
12 partial class CriticalHelper
14 internal XmlFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
16 return (XmlWriterDelegator xw, object obj, XmlObjectSerializerWriteContext ctx, ClassDataContract ctr) => new XmlFormatWriterInterpreter (classContract).WriteToXml (xw, obj, ctx, ctr);
19 internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
21 return (XmlWriterDelegator xw, object obj, XmlObjectSerializerWriteContext ctx, CollectionDataContract ctr) => new XmlFormatWriterInterpreter (collectionContract).WriteCollectionToXml (xw, obj, ctx, ctr);
26 class XmlFormatWriterInterpreter
28 public XmlFormatWriterInterpreter (ClassDataContract classContract)
30 this.classContract = classContract;
33 public XmlFormatWriterInterpreter (CollectionDataContract collectionContract)
35 this.collectionContract = collectionContract;
38 ClassDataContract classContract;
40 CollectionDataContract collectionContract;
42 XmlWriterDelegator writer = null;
44 XmlObjectSerializerWriteContext ctx = null;
45 DataContract dataContract = null;
46 object objLocal = null;
48 ClassDataContract classDataContract {
49 get { return (ClassDataContract) dataContract; }
51 CollectionDataContract collectionDataContract {
52 get {return (CollectionDataContract) dataContract; }
55 XmlDictionaryString [] contractNamespaces = null;
56 XmlDictionaryString [] memberNames = null;
57 XmlDictionaryString [] childElementNamespaces = null;
59 int childElementIndex = 0;
61 public void WriteToXml (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract dataContract)
63 this.writer = xmlWriter;
66 this.dataContract = dataContract;
68 InitArgs (classContract.UnderlyingType);
70 // DemandSerializationFormatterPermission (classContract) - irrelevant
71 // DemandMemberAccessPermission (memberAccessFlag) - irrelevant
73 if (classContract.IsReadOnlyContract)
75 DataContract.ThrowInvalidDataContractException (classContract.SerializationExceptionMessage, null);
78 WriteClass (classContract);
81 public void WriteCollectionToXml (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, CollectionDataContract collectionContract)
83 this.writer = xmlWriter;
86 this.dataContract = collectionContract;
88 InitArgs (collectionContract.UnderlyingType);
90 // DemandMemberAccessPermission(memberAccessFlag);
91 if (collectionContract.IsReadOnlyContract)
93 DataContract.ThrowInvalidDataContractException (collectionContract.SerializationExceptionMessage, null);
96 WriteCollection (collectionContract);
99 void InitArgs (Type objType)
101 if (objType == Globals.TypeOfDateTimeOffsetAdapter) {
102 objLocal = DateTimeOffsetAdapter.GetDateTimeOffsetAdapter ((DateTimeOffset) obj);
105 objLocal = CodeInterpreter.ConvertValue (obj, typeof (object), objType);
108 void InvokeOnSerializing (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext ctx)
110 if (classContract.BaseContract != null)
111 InvokeOnSerializing (classContract.BaseContract, objSerialized, ctx);
112 if (classContract.OnSerializing != null) {
113 classContract.OnSerializing.Invoke (objSerialized, new object [] {ctx.GetStreamingContext ()});
117 void InvokeOnSerialized (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext ctx)
119 if (classContract.BaseContract != null)
120 InvokeOnSerialized (classContract.BaseContract, objSerialized, ctx);
121 if (classContract.OnSerialized != null) {
122 classContract.OnSerialized.Invoke (objSerialized, new object [] {ctx.GetStreamingContext ()});
126 void WriteClass (ClassDataContract classContract)
128 InvokeOnSerializing (classContract, objLocal, ctx);
130 if (classContract.IsISerializable)
131 ctx.WriteISerializable (writer, (ISerializable) objLocal);
134 if (classContract.ContractNamespaces.Length > 1)
135 contractNamespaces = classDataContract.ContractNamespaces;
136 memberNames = classDataContract.MemberNames;
138 for (int i = 0; i < classContract.ChildElementNamespaces.Length; i++)
140 if (classContract.ChildElementNamespaces[i] != null)
142 childElementNamespaces = classDataContract.ChildElementNamespaces;
146 if (classContract.HasExtensionData)
148 ExtensionDataObject extensionData = ((IExtensibleDataObject) objLocal).ExtensionData;
149 ctx.WriteExtensionData (writer, extensionData, -1);
151 WriteMembers (classContract, extensionData, classContract);
154 WriteMembers (classContract, null, classContract);
156 InvokeOnSerialized (classContract, objLocal, ctx);
159 void WriteCollection(CollectionDataContract collectionContract)
161 XmlDictionaryString itemNamespace = dataContract.Namespace;
163 XmlDictionaryString itemName = collectionDataContract.CollectionItemName;
165 if (collectionContract.ChildElementNamespace != null)
166 writer.WriteNamespaceDecl (collectionDataContract.ChildElementNamespace);
168 if (collectionContract.Kind == CollectionKind.Array)
170 Type itemType = collectionContract.ItemType;
173 // This check does not exist in the original dynamic code,
174 // but there is no other way to check type mismatch.
175 // CollectionSerialization.ArrayContract() shows that it is required.
176 if (objLocal.GetType ().GetElementType () != itemType)
177 throw new InvalidCastException (string.Format ("Cannot cast array of {0} to array of {1}", objLocal.GetType ().GetElementType (), itemType));
179 ctx.IncrementArrayCount (writer, (Array) objLocal);
181 if (!TryWritePrimitiveArray(collectionContract.UnderlyingType, itemType, () => objLocal, itemName, itemNamespace))
183 var arr = (Array) objLocal;
184 var idx = new int [1];
185 for (i = 0; i < arr.Length; i++) {
186 if (!TryWritePrimitive(itemType, null, null, i, itemNamespace, itemName, 0)) {
187 WriteStartElement (itemType, collectionContract.Namespace, itemNamespace, itemName, 0);
189 var mbrVal = arr.GetValue (idx);
190 WriteValue (itemType, mbrVal, false);
198 // This check does not exist in the original dynamic code,
199 // but there is no other way to check type mismatch.
200 // CollectionSerialization.ArrayContract() shows that it is required.
201 if (!collectionContract.UnderlyingType.IsAssignableFrom (objLocal.GetType ()))
202 throw new InvalidCastException (string.Format ("Cannot cast {0} to {1}", objLocal.GetType (), collectionContract.UnderlyingType));
204 MethodInfo incrementCollectionCountMethod = null;
205 switch (collectionContract.Kind)
207 case CollectionKind.Collection:
208 case CollectionKind.List:
209 case CollectionKind.Dictionary:
210 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountMethod;
212 case CollectionKind.GenericCollection:
213 case CollectionKind.GenericList:
214 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(collectionContract.ItemType);
216 case CollectionKind.GenericDictionary:
217 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
220 if (incrementCollectionCountMethod != null)
221 incrementCollectionCountMethod.Invoke (ctx, new object [] {writer, objLocal});
223 bool isDictionary = false, isGenericDictionary = false;
224 Type enumeratorType = null;
225 Type [] keyValueTypes = null;
226 if (collectionContract.Kind == CollectionKind.GenericDictionary)
228 isGenericDictionary = true;
229 keyValueTypes = collectionContract.ItemType.GetGenericArguments ();
230 enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType (keyValueTypes);
232 else if (collectionContract.Kind == CollectionKind.Dictionary)
235 keyValueTypes = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
236 enumeratorType = Globals.TypeOfDictionaryEnumerator;
240 enumeratorType = collectionContract.GetEnumeratorMethod.ReturnType;
242 MethodInfo moveNextMethod = enumeratorType.GetMethod (Globals.MoveNextMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
243 MethodInfo getCurrentMethod = enumeratorType.GetMethod (Globals.GetCurrentMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
244 if (moveNextMethod == null || getCurrentMethod == null)
246 if (enumeratorType.IsInterface)
248 if (moveNextMethod == null)
249 moveNextMethod = XmlFormatGeneratorStatics.MoveNextMethod;
250 if (getCurrentMethod == null)
251 getCurrentMethod = XmlFormatGeneratorStatics.GetCurrentMethod;
255 Type ienumeratorInterface = Globals.TypeOfIEnumerator;
256 CollectionKind kind = collectionContract.Kind;
257 if (kind == CollectionKind.GenericDictionary || kind == CollectionKind.GenericCollection || kind == CollectionKind.GenericEnumerable)
259 Type[] interfaceTypes = enumeratorType.GetInterfaces();
260 foreach (Type interfaceType in interfaceTypes)
262 if (interfaceType.IsGenericType
263 && interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
264 && interfaceType.GetGenericArguments()[0] == collectionContract.ItemType)
266 ienumeratorInterface = interfaceType;
271 if (moveNextMethod == null)
272 moveNextMethod = CollectionDataContract.GetTargetMethodWithName(Globals.MoveNextMethodName, enumeratorType, ienumeratorInterface);
273 if (getCurrentMethod == null)
274 getCurrentMethod = CollectionDataContract.GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface);
277 Type elementType = getCurrentMethod.ReturnType;
278 object currentValue = null; // of elementType
280 var enumerator = (IEnumerator) collectionContract.GetEnumeratorMethod.Invoke (objLocal, new object [0]);
283 enumerator = new CollectionDataContract.DictionaryEnumerator ((IDictionaryEnumerator) enumerator);
285 else if (isGenericDictionary)
287 Type ctorParam = Globals.TypeOfIEnumeratorGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(keyValueTypes));
288 ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor(Globals.ScanAllMembers, null, new Type[] { ctorParam }, null);
289 enumerator = (IEnumerator) Activator.CreateInstance (enumeratorType, new object [] {enumerator});
292 var emptyArray = new object [0];
293 while (enumerator != null && enumerator.MoveNext ()) {
294 currentValue = getCurrentMethod.Invoke (enumerator, emptyArray);
296 if (incrementCollectionCountMethod == null)
297 XmlFormatGeneratorStatics.IncrementItemCountMethod.Invoke (ctx, new object [] {1});
299 if (!TryWritePrimitive (elementType, () => currentValue, null, null, itemNamespace, itemName, 0))
301 WriteStartElement (elementType, collectionContract.Namespace, itemNamespace, itemName, 0);
302 if (isGenericDictionary || isDictionary)
303 collectionDataContract.ItemContract.WriteXmlValue (writer, currentValue, ctx);
305 WriteValue (elementType, currentValue, false);
312 int WriteMembers (ClassDataContract classContract, ExtensionDataObject extensionData, ClassDataContract derivedMostClassContract)
314 int memberCount = (classContract.BaseContract == null) ? 0 : WriteMembers (classContract.BaseContract, extensionData, derivedMostClassContract);
316 XmlDictionaryString ns =
317 (contractNamespaces == null) ? dataContract.Namespace :
318 contractNamespaces [typeIndex - 1];
320 ctx.IncrementItemCount (classContract.Members.Count);
322 for (int i = 0; i < classContract.Members.Count; i++, memberCount++) {
324 DataMember member = classContract.Members[i];
325 Type memberType = member.MemberType;
326 object memberValue = null;
327 if (member.IsGetOnlyCollection)
328 ctx.StoreIsGetOnlyCollection ();
329 bool doWrite = true, hasMemberValue = false;
330 if (!member.EmitDefaultValue)
332 hasMemberValue = true;
333 memberValue = LoadMemberValue (member);
334 doWrite = !IsDefaultValue (memberType, memberValue);
339 bool writeXsiType = CheckIfMemberHasConflict (member, classContract, derivedMostClassContract);
340 if (writeXsiType || !TryWritePrimitive (memberType, hasMemberValue ? () => memberValue : (Func<object>) null, member.MemberInfo, null /*arrayItemIndex*/, ns, null /*nameLocal*/, i + childElementIndex)) {
341 WriteStartElement (memberType, classContract.Namespace, ns, null /*nameLocal*/, i + childElementIndex);
342 if (classContract.ChildElementNamespaces [i + childElementIndex] != null)
343 writer.WriteNamespaceDecl (childElementNamespaces [i + childElementIndex]);
344 if (memberValue == null)
345 memberValue = LoadMemberValue (member);
346 WriteValue (memberType, memberValue, writeXsiType);
350 if (classContract.HasExtensionData)
351 ctx.WriteExtensionData (writer, extensionData, memberCount);
352 } else if (!member.EmitDefaultValue) {
353 if (member.IsRequired)
354 XmlObjectSerializerWriteContext.ThrowRequiredMemberMustBeEmitted (member.Name, classContract.UnderlyingType);
359 childElementIndex += classContract.Members.Count;
364 internal bool IsDefaultValue (Type type, object value)
366 var def = GetDefaultValue (type);
367 return def == null ? (object) value == null : def.Equals (value);
370 internal object GetDefaultValue(Type type)
372 if (type.IsValueType)
374 switch (Type.GetTypeCode(type))
376 case TypeCode.Boolean:
382 case TypeCode.UInt16:
384 case TypeCode.UInt32:
387 case TypeCode.UInt64:
389 case TypeCode.Single:
391 case TypeCode.Double:
393 case TypeCode.Decimal:
394 return default (decimal);
395 case TypeCode.DateTime:
396 return default (DateTime);
402 bool CheckIfMemberHasConflict(DataMember member, ClassDataContract classContract, ClassDataContract derivedMostClassContract)
404 // Check for conflict with base type members
405 if (CheckIfConflictingMembersHaveDifferentTypes(member))
408 // Check for conflict with derived type members
409 string name = member.Name;
410 string ns = classContract.StableName.Namespace;
411 ClassDataContract currentContract = derivedMostClassContract;
412 while (currentContract != null && currentContract != classContract)
414 if (ns == currentContract.StableName.Namespace)
416 List<DataMember> members = currentContract.Members;
417 for (int j = 0; j < members.Count; j++)
419 if (name == members[j].Name)
420 return CheckIfConflictingMembersHaveDifferentTypes(members[j]);
423 currentContract = currentContract.BaseContract;
429 bool CheckIfConflictingMembersHaveDifferentTypes(DataMember member)
431 while (member.ConflictingMember != null)
433 if (member.MemberType != member.ConflictingMember.MemberType)
435 member = member.ConflictingMember;
440 bool NeedsPrefix(Type type, XmlDictionaryString ns)
442 return type == Globals.TypeOfXmlQualifiedName && (ns != null && ns.Value != null && ns.Value.Length > 0);
445 void WriteStartElement (Type type, XmlDictionaryString ns, XmlDictionaryString namespaceLocal, XmlDictionaryString nameLocal, int nameIndex)
447 bool needsPrefix = NeedsPrefix(type, ns);
448 nameLocal = nameLocal ?? memberNames [nameIndex];
450 writer.WriteStartElement (Globals.ElementPrefix, nameLocal, namespaceLocal);
452 writer.WriteStartElement (nameLocal, namespaceLocal);
455 void WriteEndElement ()
457 writer.WriteEndElement ();
460 void WriteValue (Type memberType, object memberValue, bool writeXsiType)
462 Pointer memberValueRefPointer = null;
463 if (memberType.IsPointer)
464 memberValueRefPointer = (Pointer) XmlFormatGeneratorStatics.BoxPointer.Invoke (null, new object [] {memberValue, memberType});
465 bool isNullableOfT = (memberType.IsGenericType &&
466 memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable);
467 if (memberType.IsValueType && !isNullableOfT)
469 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
470 if (primitiveContract != null && !writeXsiType)
471 primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
473 // InternalSerialize(XmlFormatGeneratorStatics.InternalSerializeMethod, () => memberValue, memberType, writeXsiType);
474 var typeHandleValue = Type.GetTypeHandle (memberValue);
475 var isDeclaredType = typeHandleValue.Equals (CodeInterpreter.ConvertValue (memberValue, memberType, Globals.TypeOfObject));
477 ctx.InternalSerialize (writer, memberValue, isDeclaredType, writeXsiType, DataContract.GetId (memberType.TypeHandle), memberType.TypeHandle);
484 memberValue = UnwrapNullableObject(() => memberValue, ref memberType, out isNull); //Leaves !HasValue on stack
486 isNull = memberValue == null;
488 XmlFormatGeneratorStatics.WriteNullMethod.Invoke (ctx, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
490 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
491 if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject && !writeXsiType) {
493 primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
495 primitiveContract.XmlFormatContentWriterMethod.Invoke (ctx, new object [] {writer, memberValue});
497 bool isNull2 = false;
498 if (memberType == Globals.TypeOfObject || //boxed Nullable<T>
499 memberType == Globals.TypeOfValueType ||
500 ((IList)Globals.TypeOfNullable.GetInterfaces()).Contains(memberType)) {
501 var unwrappedMemberValue = CodeInterpreter.ConvertValue (memberValue, memberType.GetType (), Globals.TypeOfObject);
502 memberValue = unwrappedMemberValue;
503 isNull2 = memberValue == null;
506 XmlFormatGeneratorStatics.WriteNullMethod.Invoke (ctx, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
508 var typeHandleValue = Type.GetTypeHandle (memberValue);
509 var isDeclaredType = typeHandleValue.Equals (CodeInterpreter.ConvertValue (memberValue, memberType, Globals.TypeOfObject));
511 ctx.InternalSerialize (writer, memberValue, isDeclaredType, writeXsiType, DataContract.GetId (memberType.TypeHandle), memberType.TypeHandle);
513 ctx.InternalSerializeReference (writer, memberValue, isDeclaredType, writeXsiType, DataContract.GetId (memberType.TypeHandle), memberType.TypeHandle);
514 //InternalSerialize((isNullableOfT ? XmlFormatGeneratorStatics.InternalSerializeMethod : XmlFormatGeneratorStatics.InternalSerializeReferenceMethod), () => memberValue, memberType, writeXsiType);
521 object UnwrapNullableObject(Func<object> memberValue, ref Type memberType, out bool isNull)// Leaves !HasValue on stack
523 object v = memberValue ();
525 while (memberType.IsGenericType && memberType.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
526 Type innerType = memberType.GetGenericArguments () [0];
527 if ((bool) XmlFormatGeneratorStatics.GetHasValueMethod.MakeGenericMethod (innerType).Invoke (null, new object [] {v}))
528 v = XmlFormatGeneratorStatics.GetNullableValueMethod.MakeGenericMethod (innerType).Invoke (null, new object [] {v});
531 v = XmlFormatGeneratorStatics.GetDefaultValueMethod.MakeGenericMethod (memberType).Invoke (null, new object [0]);
533 memberType = innerType;
539 bool TryWritePrimitive(Type type, Func<object> value, MemberInfo memberInfo, int? arrayItemIndex, XmlDictionaryString ns, XmlDictionaryString name, int nameIndex)
541 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
542 if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
545 object callee = null;
546 var args = new List<object> ();
549 if (type.IsValueType)
555 // load primitive value
558 else if (memberInfo != null)
559 args.Add (CodeInterpreter.GetMember (memberInfo, objLocal));
561 args.Add (((Array) objLocal).GetValue (new int [] {(int) arrayItemIndex}));
566 args.Add (memberNames [nameIndex]);
569 // call method to write primitive
570 primitiveContract.XmlFormatWriterMethod.Invoke (callee, args.ToArray ());
574 bool TryWritePrimitiveArray (Type type, Type itemType, Func<object> value, XmlDictionaryString itemName, XmlDictionaryString itemNamespace)
576 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
577 if (primitiveContract == null)
580 string writeArrayMethod = null;
581 switch (Type.GetTypeCode(itemType))
583 case TypeCode.Boolean:
584 writeArrayMethod = "WriteBooleanArray";
586 case TypeCode.DateTime:
587 writeArrayMethod = "WriteDateTimeArray";
589 case TypeCode.Decimal:
590 writeArrayMethod = "WriteDecimalArray";
593 writeArrayMethod = "WriteInt32Array";
596 writeArrayMethod = "WriteInt64Array";
598 case TypeCode.Single:
599 writeArrayMethod = "WriteSingleArray";
601 case TypeCode.Double:
602 writeArrayMethod = "WriteDoubleArray";
607 if (writeArrayMethod != null)
609 typeof (XmlWriterDelegator).GetMethod (writeArrayMethod, Globals.ScanAllMembers, null, new Type[] { type, typeof (XmlDictionaryString), typeof (XmlDictionaryString) }, null).Invoke (writer, new object [] {value (), itemName, itemNamespace});
615 object LoadMemberValue (DataMember member)
617 return CodeInterpreter.GetMember (member.MemberInfo, objLocal);