Merge pull request #1860 from saper/tz-fix
[mono.git] / mcs / class / System.Runtime.Serialization / ReferenceSources / JsonFormatWriterGenerator_static.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Reflection;
6 using System.Xml;
7
8 namespace System.Runtime.Serialization.Json
9 {
10         internal partial class JsonFormatWriterGenerator
11         {
12                 partial class CriticalHelper
13                 {
14                         internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
15                         {
16                                 return (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString [] memberNames) => new JsonFormatWriterInterpreter (classContract).WriteToJson (xmlWriter, obj, context, dataContract, memberNames);
17                         }
18                         internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
19                         {
20                                 return (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract) => new JsonFormatWriterInterpreter (collectionContract).WriteCollectionToJson (xmlWriter, obj, context, dataContract);
21                         }
22                 }
23         }
24
25         class JsonFormatWriterInterpreter
26         {
27                 public JsonFormatWriterInterpreter (ClassDataContract classContract)
28                 {
29                         this.classContract = classContract;
30                 }
31
32                 public JsonFormatWriterInterpreter (CollectionDataContract collectionContract)
33                 {
34                         this.collectionContract = collectionContract;
35                 }
36
37                 ClassDataContract classContract;
38
39                 CollectionDataContract collectionContract;
40
41                 XmlWriterDelegator writer = null;
42                 object obj = null;
43                 XmlObjectSerializerWriteContextComplexJson context = null;
44                 DataContract dataContract = null;
45                 object objLocal = null;
46
47                 ClassDataContract classDataContract {
48                         get { return (ClassDataContract) dataContract; }
49                 }
50                 CollectionDataContract collectionDataContract {
51                         get {return (CollectionDataContract) dataContract; }
52                 }
53
54                 XmlDictionaryString [] memberNames = null;
55                 int typeIndex = 1;
56                 int childElementIndex = 0;
57
58                 public void WriteToJson (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString [] memberNames)
59                 {
60                         this.writer = xmlWriter;
61                         this.obj = obj;
62                         this.context = context;
63                         this.dataContract = dataContract;
64                         this.memberNames = memberNames;
65
66                         InitArgs (classContract.UnderlyingType);
67
68                         // DemandSerializationFormatterPermission (classContract) - irrelevant
69                         // DemandMemberAccessPermission (memberAccessFlag) - irrelevant
70
71                         if (classContract.IsReadOnlyContract)
72                         {
73                                 DataContract.ThrowInvalidDataContractException (classContract.SerializationExceptionMessage, null);
74                         }
75
76                         WriteClass (classContract);
77                 }
78
79                 public void WriteCollectionToJson (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract)
80                 {
81                         this.writer = xmlWriter;
82                         this.obj = obj;
83                         this.context = context;
84                         this.dataContract = dataContract;
85
86                         InitArgs (collectionContract.UnderlyingType);                   
87
88                         // DemandMemberAccessPermission(memberAccessFlag);
89                         if (collectionContract.IsReadOnlyContract)
90                         {
91                                 DataContract.ThrowInvalidDataContractException (collectionContract.SerializationExceptionMessage, null);
92                         }
93
94                         WriteCollection (collectionContract);
95                 }
96
97                 void InitArgs (Type objType)
98                 {
99                         if (objType == Globals.TypeOfDateTimeOffsetAdapter) {
100                                 objLocal = DateTimeOffsetAdapter.GetDateTimeOffsetAdapter ((DateTimeOffset) obj);
101                         }
102                         else
103                                 objLocal = CodeInterpreter.ConvertValue (obj, typeof (object), objType);
104                 }
105
106                 void InvokeOnSerializing (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext context)
107                 {
108                         if (classContract.BaseContract != null)
109                                 InvokeOnSerializing (classContract.BaseContract, objSerialized, context);
110                         if (classContract.OnSerializing != null) {
111                                 classContract.OnSerializing.Invoke (objSerialized, new object [] {context.GetStreamingContext ()});
112                         }
113                 }
114
115                 void InvokeOnSerialized (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext context)
116                 {
117                         if (classContract.BaseContract != null)
118                                 InvokeOnSerialized (classContract.BaseContract, objSerialized, context);
119                         if (classContract.OnSerialized != null) {
120                                 classContract.OnSerialized.Invoke (objSerialized, new object [] {context.GetStreamingContext ()});
121                         }
122                 }
123
124                 void WriteClass (ClassDataContract classContract)
125                 {
126                         InvokeOnSerializing (classContract, objLocal, context);
127
128                         if (classContract.IsISerializable)
129                                 context.WriteJsonISerializable (writer, (ISerializable) objLocal);
130                         else
131                         {
132                                 if (classContract.HasExtensionData)
133                                 {
134                                         ExtensionDataObject extensionData = ((IExtensibleDataObject) objLocal).ExtensionData;
135                                         context.WriteExtensionData (writer, extensionData, -1);
136
137                                         WriteMembers (classContract, extensionData, classContract);
138                                 }
139                                 else
140                                         WriteMembers (classContract, null, classContract);
141                         }
142                         InvokeOnSerialized (classContract, objLocal, context);
143                 }
144
145                 void WriteCollection(CollectionDataContract collectionContract)
146                 {
147                         XmlDictionaryString itemName = context.CollectionItemName;
148
149                         if (collectionContract.Kind == CollectionKind.Array)
150                         {
151                                 Type itemType = collectionContract.ItemType;
152                                 int i;
153
154                                 // This check does not exist in the original dynamic code,
155                                 // but there is no other way to check type mismatch.
156                                 // CollectionSerialization.ArrayContract() shows that it is required.
157                                 if (objLocal.GetType ().GetElementType () != itemType)
158                                         throw new InvalidCastException (string.Format ("Cannot cast array of {0} to array of {1}", objLocal.GetType ().GetElementType (), itemType));
159
160                                 context.IncrementArrayCount (writer, (Array) objLocal);
161
162                                 if (!TryWritePrimitiveArray(collectionContract.UnderlyingType, itemType, () => objLocal, itemName))
163                                 {
164                                         WriteArrayAttribute ();
165                                         var arr = (Array) objLocal;
166                                         var idx = new int [1];
167                                         for (i = 0; i < arr.Length; i++) {
168                                                 if (!TryWritePrimitive(itemType, null, null, i, itemName, 0)) {
169                                                         WriteStartElement (itemName, 0);
170                                                         idx [0] = i;
171                                                         var mbrVal = arr.GetValue (idx);
172                                                         WriteValue (itemType, mbrVal);
173                                                         WriteEndElement ();
174                                                 }
175                                         }
176                                 }
177                         }
178                         else
179                         {
180                                 // This check does not exist in the original dynamic code,
181                                 // but there is no other way to check type mismatch.
182                                 // CollectionSerialization.ArrayContract() shows that it is required.
183                                 if (!collectionContract.UnderlyingType.IsAssignableFrom (objLocal.GetType ()))
184                                         throw new InvalidCastException (string.Format ("Cannot cast {0} to {1}", objLocal.GetType (), collectionContract.UnderlyingType));
185                                 
186                                 MethodInfo incrementCollectionCountMethod = null;
187                                 switch (collectionContract.Kind)
188                                 {
189                                 case CollectionKind.Collection:
190                                 case CollectionKind.List:
191                                 case CollectionKind.Dictionary:
192                                         incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountMethod;
193                                         break;
194                                 case CollectionKind.GenericCollection:
195                                 case CollectionKind.GenericList:
196                                         incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(collectionContract.ItemType);
197                                         break;
198                                 case CollectionKind.GenericDictionary:
199                                         incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
200                                         break;
201                                 }
202                                 if (incrementCollectionCountMethod != null)
203                                         incrementCollectionCountMethod.Invoke (context, new object [] {writer, objLocal});
204
205                                 bool isDictionary = false, isGenericDictionary = false;
206                                 Type enumeratorType = null;
207                                 Type [] keyValueTypes = null;
208                                 if (collectionContract.Kind == CollectionKind.GenericDictionary)
209                                 {
210                                         isGenericDictionary = true;
211                                         keyValueTypes = collectionContract.ItemType.GetGenericArguments ();
212                                         enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType (keyValueTypes);
213                                 }
214                                 else if (collectionContract.Kind == CollectionKind.Dictionary)
215                                 {
216                                         isDictionary = true;
217                                         keyValueTypes = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
218                                         enumeratorType = Globals.TypeOfDictionaryEnumerator;
219                                 }
220                                 else
221                                 {
222                                         enumeratorType = collectionContract.GetEnumeratorMethod.ReturnType;
223                                 }
224                                 MethodInfo moveNextMethod = enumeratorType.GetMethod (Globals.MoveNextMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
225                                 MethodInfo getCurrentMethod = enumeratorType.GetMethod (Globals.GetCurrentMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
226                                 if (moveNextMethod == null || getCurrentMethod == null)
227                                 {
228                                         if (enumeratorType.IsInterface)
229                                         {
230                                                 if (moveNextMethod == null)
231                                                         moveNextMethod = JsonFormatGeneratorStatics.MoveNextMethod;
232                                                 if (getCurrentMethod == null)
233                                                         getCurrentMethod = JsonFormatGeneratorStatics.GetCurrentMethod;
234                                         }
235                                         else
236                                         {
237                                                 Type ienumeratorInterface = Globals.TypeOfIEnumerator;
238                                                 CollectionKind kind = collectionContract.Kind;
239                                                 if (kind == CollectionKind.GenericDictionary || kind == CollectionKind.GenericCollection || kind == CollectionKind.GenericEnumerable)
240                                                 {
241                                                         Type[] interfaceTypes = enumeratorType.GetInterfaces();
242                                                         foreach (Type interfaceType in interfaceTypes)
243                                                         {
244                                                                 if (interfaceType.IsGenericType
245                                                                         && interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
246                                                                         && interfaceType.GetGenericArguments()[0] == collectionContract.ItemType)
247                                                                 {
248                                                                         ienumeratorInterface = interfaceType;
249                                                                         break;
250                                                                 }
251                                                         }
252                                                 }
253                                                 if (moveNextMethod == null)
254                                                         moveNextMethod = CollectionDataContract.GetTargetMethodWithName(Globals.MoveNextMethodName, enumeratorType, ienumeratorInterface);
255                                                 if (getCurrentMethod == null)
256                                                         getCurrentMethod = CollectionDataContract.GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface);
257                                         }
258                                 }
259                                 Type elementType = getCurrentMethod.ReturnType;
260                                 object currentValue = null; // of elementType
261
262                                 var enumerator = (IEnumerator) collectionContract.GetEnumeratorMethod.Invoke (objLocal, new object [0]);
263                                 if (isDictionary)
264                                 {
265                                         ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor (Globals.ScanAllMembers, null, new Type[] { Globals.TypeOfIDictionaryEnumerator }, null);
266                                         enumerator = (IEnumerator) dictEnumCtor.Invoke (new object [] {enumerator});
267                                 }
268                                 else if (isGenericDictionary)
269                                 {
270                                         Type ctorParam = Globals.TypeOfIEnumeratorGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(keyValueTypes));
271                                         ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor(Globals.ScanAllMembers, null, new Type[] { ctorParam }, null);
272                                         enumerator = (IEnumerator) Activator.CreateInstance (enumeratorType, new object [] {enumerator});
273                                 }
274
275                                 bool canWriteSimpleDictionary = isDictionary || isGenericDictionary;
276                                 
277                                 bool writeSimpleDictionary = canWriteSimpleDictionary && context.UseSimpleDictionaryFormat;
278                                 PropertyInfo genericDictionaryKeyProperty = null, genericDictionaryValueProperty = null;
279                                 
280                                 if (canWriteSimpleDictionary)
281                                 {
282                                         Type genericDictionaryKeyValueType = Globals.TypeOfKeyValue.MakeGenericType (keyValueTypes);
283                                         genericDictionaryKeyProperty = genericDictionaryKeyValueType.GetProperty (JsonGlobals.KeyString);
284                                         genericDictionaryValueProperty = genericDictionaryKeyValueType.GetProperty (JsonGlobals.ValueString);
285                                 }
286
287                                 if (writeSimpleDictionary) {
288                                         WriteObjectAttribute ();
289                                         object key, value;
290                                         var empty_args = new object [0];
291                                         while ((bool) moveNextMethod.Invoke (enumerator, empty_args)) {
292                                                 currentValue = getCurrentMethod.Invoke (enumerator, empty_args);
293                                                 key = CodeInterpreter.GetMember (genericDictionaryKeyProperty, currentValue);
294                                                 value = CodeInterpreter.GetMember (genericDictionaryValueProperty, currentValue);
295
296                                                 WriteStartElement (key, 0 /*nameIndex*/);
297                                                 WriteValue (genericDictionaryValueProperty.PropertyType, value);
298                                                 WriteEndElement ();
299                                         }
300                                 } else {
301                                         WriteArrayAttribute ();
302
303                                         var emptyArray = new object [0];
304                                         while (enumerator != null && enumerator.MoveNext ()) {
305                                                 currentValue = getCurrentMethod.Invoke (enumerator, emptyArray);
306
307                                                 if (incrementCollectionCountMethod == null)
308                                                         XmlFormatGeneratorStatics.IncrementItemCountMethod.Invoke (context, new object [] {1});
309
310                                                 if (!TryWritePrimitive (elementType, () => currentValue, null, null, itemName, 0))
311                                                 {
312                                                         WriteStartElement (itemName, 0);
313                                                         if (isGenericDictionary || isDictionary) {
314                                                                 var jc = JsonDataContract.GetJsonDataContract (XmlObjectSerializerWriteContextComplexJson.GetRevisedItemContract (
315                                                                 collectionDataContract.ItemContract));
316                                                                 // FIXME: this TypeHandle might be wrong; there is no easy way to get Type for currentValue though.
317                                                                 DataContractJsonSerializer.WriteJsonValue (jc, writer, currentValue, context, currentValue.GetType ().TypeHandle);
318                                                         }
319                                                         else
320                                                                 WriteValue (elementType, currentValue);
321                                                         WriteEndElement ();
322                                                 }
323                                         }
324                                 }
325                         }
326                 }
327
328                 int WriteMembers (ClassDataContract classContract, ExtensionDataObject extensionData, ClassDataContract derivedMostClassContract)
329                 {
330                         int memberCount = (classContract.BaseContract == null) ? 0 : WriteMembers (classContract.BaseContract, extensionData, derivedMostClassContract);
331
332                         context.IncrementItemCount (classContract.Members.Count);
333
334                         for (int i = 0; i < classContract.Members.Count; i++, memberCount++) {
335
336                                 DataMember member = classContract.Members[i];
337                                 Type memberType = member.MemberType;
338                                 object memberValue = null;
339                                 if (member.IsGetOnlyCollection)
340                                         context.StoreIsGetOnlyCollection ();
341                                 bool doWrite = true, hasMemberValue = false;
342                                 if (!member.EmitDefaultValue)
343                                 {
344                                         hasMemberValue = true;
345                                         memberValue = LoadMemberValue (member);
346                                         doWrite = !IsDefaultValue (memberType, memberValue);
347                                 }
348
349                                 if (doWrite) {
350
351                                         bool requiresNameAttribute = DataContractJsonSerializer.CheckIfXmlNameRequiresMapping (classContract.MemberNames [i]);
352                                         
353                                         if (requiresNameAttribute || !TryWritePrimitive(memberType, hasMemberValue ? () => memberValue : (Func<object>) null, member.MemberInfo, null /*arrayItemIndex*/, null /*nameLocal*/, i + childElementIndex)) {
354
355                                                 // Note: DataContractSerializer has member-conflict logic here to deal with the schema export
356                                                 //       requirement that the same member can't be of two different types.
357                                                 if (requiresNameAttribute)
358                                                         XmlObjectSerializerWriteContextComplexJson.WriteJsonNameWithMapping (writer, memberNames, i + childElementIndex);
359                                                 else
360                                                         WriteStartElement (null /*nameLocal*/, i + childElementIndex);
361
362                                                 if (memberValue == null)
363                                                         memberValue = LoadMemberValue (member);
364                                                 WriteValue (memberType, memberValue);
365                                                 WriteEndElement ();
366                                         }
367
368                                         if (classContract.HasExtensionData)
369                                                 context.WriteExtensionData (writer, extensionData, memberCount);
370                                 } else if (!member.EmitDefaultValue) {
371                                         if (member.IsRequired)
372                                                 XmlObjectSerializerWriteContext.ThrowRequiredMemberMustBeEmitted (member.Name, classContract.UnderlyingType);
373                                 }
374                         }
375
376                         typeIndex++;
377                         childElementIndex += classContract.Members.Count;
378                         return memberCount;
379                 }
380
381
382                 internal bool IsDefaultValue (Type type, object value)
383                 {
384                         var def = GetDefaultValue (type);
385                         return def == null ? (object) value == null : def.Equals (value);
386                 }
387
388                 internal object GetDefaultValue(Type type)
389                 {
390                         if (type.IsValueType)
391                         {
392                                 switch (Type.GetTypeCode(type))
393                                 {
394                                 case TypeCode.Boolean:
395                                         return false;
396                                 case TypeCode.Char:
397                                 case TypeCode.SByte:
398                                 case TypeCode.Byte:
399                                 case TypeCode.Int16:
400                                 case TypeCode.UInt16:
401                                 case TypeCode.Int32:
402                                 case TypeCode.UInt32:
403                                         return 0;
404                                 case TypeCode.Int64:
405                                 case TypeCode.UInt64:
406                                         return 0L;
407                                 case TypeCode.Single:
408                                         return 0.0F;
409                                 case TypeCode.Double:
410                                         return 0.0;
411                                 case TypeCode.Decimal:
412                                         return default (decimal);
413                                 case TypeCode.DateTime:
414                                         return default (DateTime);
415                                 }
416                         }
417                         return null;
418                 }
419
420                 void WriteStartElement (object nameLocal, int nameIndex)
421                 {
422                         var name = nameLocal ?? memberNames [nameIndex];
423                         XmlDictionaryString namespaceLocal = null;
424                         if (nameLocal != null && nameLocal is string)
425                                 writer.WriteStartElement ((string) name, null);
426                         else
427                                 writer.WriteStartElement ((XmlDictionaryString) name, null);
428                 }
429
430                 void WriteEndElement ()
431                 {
432                         writer.WriteEndElement ();
433                 }
434
435                 void WriteArrayAttribute ()
436                 {
437                         writer.WriteAttributeString (
438                                 null /* prefix */,
439                                 JsonGlobals.typeString /* local name */,
440                                 string.Empty /* namespace */,
441                                 JsonGlobals.arrayString /* value */);
442                 }
443
444                 void WriteObjectAttribute ()
445                 {
446                         writer.WriteAttributeString (
447                                 null /* prefix */,
448                                 JsonGlobals.typeString /* local name */,
449                                 null /* namespace */,
450                                 JsonGlobals.objectString /* value */);
451                 }
452
453                 void WriteValue (Type memberType, object memberValue)
454                 {
455                         Pointer memberValueRefPointer = null;
456                         if (memberType.IsPointer)
457                                 memberValueRefPointer = (Pointer) JsonFormatGeneratorStatics.BoxPointer.Invoke (null, new object [] {memberValue, memberType});
458                         bool isNullableOfT = (memberType.IsGenericType &&
459                                 memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable);
460                         if (memberType.IsValueType && !isNullableOfT)
461                         {
462                                 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
463                                 if (primitiveContract != null)
464                                         primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
465                                 else
466                                         InternalSerialize (XmlFormatGeneratorStatics.InternalSerializeMethod, () => memberValue, memberType, false);
467                         }
468                         else
469                         {
470                                 bool isNull;
471                                 if (isNullableOfT)
472                                         memberValue = UnwrapNullableObject(() => memberValue, ref memberType, out isNull); //Leaves !HasValue on stack
473                                 else
474                                         isNull = memberValue == null;
475                                 if (isNull)
476                                         XmlFormatGeneratorStatics.WriteNullMethod.Invoke (context, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
477                                 else {
478                                         PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
479                                         if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
480                                                 if (isNullableOfT)
481                                                         primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
482                                                 else                                                    
483                                                         primitiveContract.XmlFormatContentWriterMethod.Invoke (context, new object [] {writer, memberValue});
484                                         } else {
485                                                 bool isNull2 = false;
486                                                 if (memberType == Globals.TypeOfObject || //boxed Nullable<T>
487                                                         memberType == Globals.TypeOfValueType ||
488                                                         ((IList)Globals.TypeOfNullable.GetInterfaces()).Contains(memberType)) {
489                                                         var unwrappedMemberValue = CodeInterpreter.ConvertValue (memberValue, memberType.GetType (), Globals.TypeOfObject);
490                                                         memberValue = unwrappedMemberValue;
491                                                         isNull2 = memberValue == null;
492                                                 }
493                                                 if (isNull2) {
494                                                         XmlFormatGeneratorStatics.WriteNullMethod.Invoke (context, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
495                                                 } else {
496                                                         InternalSerialize((isNullableOfT ? XmlFormatGeneratorStatics.InternalSerializeMethod : XmlFormatGeneratorStatics.InternalSerializeReferenceMethod),
497                                                                 () => memberValue, memberType, false);
498                                                 }
499                                         }
500                                 }
501                         }
502                 }
503
504                 void InternalSerialize (MethodInfo methodInfo, Func<object> memberValue, Type memberType, bool writeXsiType)
505                 {
506                         var v = memberValue ();
507                         var typeHandleValue = Type.GetTypeHandle (v);
508                         var isDeclaredType = typeHandleValue.Equals (CodeInterpreter.ConvertValue (v, memberType, Globals.TypeOfObject));
509                         try {
510                                 methodInfo.Invoke (context, new object [] {writer, memberValue != null ? v : null, isDeclaredType, writeXsiType, DataContract.GetId (memberType.TypeHandle), memberType.TypeHandle});
511                         } catch (TargetInvocationException ex) {
512                                 if (ex.InnerException != null)
513                                         throw ex.InnerException;
514                                 else
515                                         throw;
516                         }
517                 }
518
519                 object UnwrapNullableObject(Func<object> memberValue, ref Type memberType, out bool isNull)// Leaves !HasValue on stack
520                 {
521                         object v = memberValue ();
522                         isNull = false;
523                         while (memberType.IsGenericType && memberType.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
524                                 Type innerType = memberType.GetGenericArguments () [0];
525                                 if ((bool) XmlFormatGeneratorStatics.GetHasValueMethod.MakeGenericMethod (innerType).Invoke (null, new object [] {v}))
526                                         v = XmlFormatGeneratorStatics.GetNullableValueMethod.MakeGenericMethod (innerType).Invoke (null, new object [] {v});
527                                 else {
528                                         isNull = true;
529                                         v = XmlFormatGeneratorStatics.GetDefaultValueMethod.MakeGenericMethod (memberType).Invoke (null, new object [0]);
530                                 }
531                                 memberType = innerType;
532                         }
533                         
534                         return v;
535                 }
536
537                 bool TryWritePrimitive(Type type, Func<object> value, MemberInfo memberInfo, int? arrayItemIndex, XmlDictionaryString name, int nameIndex)
538                 {
539                         PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
540                         if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
541                                 return false;
542
543                         object callee = null;
544                         var args = new List<object> ();
545
546                         // load writer
547                         if (type.IsValueType)
548                                 callee = writer;
549                         else {
550                                 callee = context;
551                                 args.Add (writer);
552                         }
553                         // load primitive value 
554                         if (value != null)
555                                 args.Add (value ());
556                         else if (memberInfo != null)
557                                 args.Add (CodeInterpreter.GetMember (memberInfo, objLocal));
558                         else
559                                 args.Add (((Array) objLocal).GetValue (new int [] {(int) arrayItemIndex}));
560                         // load name
561                         if (name != null)
562                                 args.Add (name);
563                         else
564                                 args.Add (memberNames [nameIndex]);
565                         // load namespace
566                         args.Add (null);
567                         // call method to write primitive
568                         primitiveContract.XmlFormatWriterMethod.Invoke (callee, args.ToArray ());
569                         return true;
570                 }
571
572                 bool TryWritePrimitiveArray (Type type, Type itemType, Func<object> value, XmlDictionaryString itemName)
573                 {
574                         PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
575                         if (primitiveContract == null)
576                                 return false;
577
578                         string writeArrayMethod = null;
579                         switch (Type.GetTypeCode(itemType))
580                         {
581                         case TypeCode.Boolean:
582                                 writeArrayMethod = "WriteJsonBooleanArray";
583                                 break;
584                         case TypeCode.DateTime:
585                                 writeArrayMethod = "WriteJsonDateTimeArray";
586                                 break;
587                         case TypeCode.Decimal:
588                                 writeArrayMethod = "WriteJsonDecimalArray";
589                                 break;
590                         case TypeCode.Int32:
591                                 writeArrayMethod = "WriteJsonInt32Array";
592                                 break;
593                         case TypeCode.Int64:
594                                 writeArrayMethod = "WriteJsonInt64Array";
595                                 break;
596                         case TypeCode.Single:
597                                 writeArrayMethod = "WriteJsonSingleArray";
598                                 break;
599                         case TypeCode.Double:
600                                 writeArrayMethod = "WriteJsonDoubleArray";
601                                 break;
602                         default:
603                                 break;
604                         }
605                         if (writeArrayMethod != null)
606                         {
607                                 WriteArrayAttribute ();
608                                 typeof(JsonWriterDelegator).GetMethod(writeArrayMethod, Globals.ScanAllMembers, null, new Type[] { type, typeof(XmlDictionaryString), typeof(XmlDictionaryString) }, null).Invoke (writer, new object [] {value (), itemName, null});
609                                 return true;
610                         }
611                         return false;
612                 }
613
614                 object LoadMemberValue (DataMember member)
615                 {
616                         return CodeInterpreter.GetMember (member.MemberInfo, objLocal);
617                 }
618         }
619 }
620