2 using System.Collections;
3 using System.Collections.Generic;
5 using System.Reflection;
8 namespace System.Runtime.Serialization.Json
10 internal partial class JsonFormatWriterGenerator
12 partial class CriticalHelper
14 internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
16 return (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString [] memberNames) => new JsonFormatWriterInterpreter (classContract).WriteToJson (xmlWriter, obj, context, dataContract, memberNames);
18 internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
20 return (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract) => new JsonFormatWriterInterpreter (collectionContract).WriteCollectionToJson (xmlWriter, obj, context, dataContract);
25 class JsonFormatWriterInterpreter
27 public JsonFormatWriterInterpreter (ClassDataContract classContract)
29 this.classContract = classContract;
32 public JsonFormatWriterInterpreter (CollectionDataContract collectionContract)
34 this.collectionContract = collectionContract;
37 ClassDataContract classContract;
39 CollectionDataContract collectionContract;
41 XmlWriterDelegator writer = null;
43 XmlObjectSerializerWriteContextComplexJson context = null;
44 DataContract dataContract = null;
45 object objLocal = null;
47 ClassDataContract classDataContract {
48 get { return (ClassDataContract) dataContract; }
50 CollectionDataContract collectionDataContract {
51 get {return (CollectionDataContract) dataContract; }
54 XmlDictionaryString [] memberNames = null;
56 int childElementIndex = 0;
58 public void WriteToJson (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString [] memberNames)
60 this.writer = xmlWriter;
62 this.context = context;
63 this.dataContract = dataContract;
64 this.memberNames = memberNames;
66 InitArgs (classContract.UnderlyingType);
68 // DemandSerializationFormatterPermission (classContract) - irrelevant
69 // DemandMemberAccessPermission (memberAccessFlag) - irrelevant
71 if (classContract.IsReadOnlyContract)
73 DataContract.ThrowInvalidDataContractException (classContract.SerializationExceptionMessage, null);
76 WriteClass (classContract);
79 public void WriteCollectionToJson (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract)
81 this.writer = xmlWriter;
83 this.context = context;
84 this.dataContract = dataContract;
86 InitArgs (collectionContract.UnderlyingType);
88 // DemandMemberAccessPermission(memberAccessFlag);
89 if (collectionContract.IsReadOnlyContract)
91 DataContract.ThrowInvalidDataContractException (collectionContract.SerializationExceptionMessage, null);
94 WriteCollection (collectionContract);
97 void InitArgs (Type objType)
99 if (objType == Globals.TypeOfDateTimeOffsetAdapter) {
100 objLocal = DateTimeOffsetAdapter.GetDateTimeOffsetAdapter ((DateTimeOffset) obj);
103 objLocal = CodeInterpreter.ConvertValue (obj, typeof (object), objType);
106 void InvokeOnSerializing (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext context)
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 ()});
115 void InvokeOnSerialized (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext context)
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 ()});
124 void WriteClass (ClassDataContract classContract)
126 InvokeOnSerializing (classContract, objLocal, context);
128 if (classContract.IsISerializable)
129 context.WriteJsonISerializable (writer, (ISerializable) objLocal);
132 if (classContract.HasExtensionData)
134 ExtensionDataObject extensionData = ((IExtensibleDataObject) objLocal).ExtensionData;
135 context.WriteExtensionData (writer, extensionData, -1);
137 WriteMembers (classContract, extensionData, classContract);
140 WriteMembers (classContract, null, classContract);
142 InvokeOnSerialized (classContract, objLocal, context);
145 void WriteCollection(CollectionDataContract collectionContract)
147 XmlDictionaryString itemName = context.CollectionItemName;
149 if (collectionContract.Kind == CollectionKind.Array)
151 Type itemType = collectionContract.ItemType;
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));
160 context.IncrementArrayCount (writer, (Array) objLocal);
162 if (!TryWritePrimitiveArray(collectionContract.UnderlyingType, itemType, () => objLocal, itemName))
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);
171 var mbrVal = arr.GetValue (idx);
172 WriteValue (itemType, mbrVal);
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));
186 MethodInfo incrementCollectionCountMethod = null;
187 switch (collectionContract.Kind)
189 case CollectionKind.Collection:
190 case CollectionKind.List:
191 case CollectionKind.Dictionary:
192 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountMethod;
194 case CollectionKind.GenericCollection:
195 case CollectionKind.GenericList:
196 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(collectionContract.ItemType);
198 case CollectionKind.GenericDictionary:
199 incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
202 if (incrementCollectionCountMethod != null)
203 incrementCollectionCountMethod.Invoke (context, new object [] {writer, objLocal});
205 bool isDictionary = false, isGenericDictionary = false;
206 Type enumeratorType = null;
207 Type [] keyValueTypes = null;
208 if (collectionContract.Kind == CollectionKind.GenericDictionary)
210 isGenericDictionary = true;
211 keyValueTypes = collectionContract.ItemType.GetGenericArguments ();
212 enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType (keyValueTypes);
214 else if (collectionContract.Kind == CollectionKind.Dictionary)
217 keyValueTypes = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
218 enumeratorType = Globals.TypeOfDictionaryEnumerator;
222 enumeratorType = collectionContract.GetEnumeratorMethod.ReturnType;
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)
228 if (enumeratorType.IsInterface)
230 if (moveNextMethod == null)
231 moveNextMethod = JsonFormatGeneratorStatics.MoveNextMethod;
232 if (getCurrentMethod == null)
233 getCurrentMethod = JsonFormatGeneratorStatics.GetCurrentMethod;
237 Type ienumeratorInterface = Globals.TypeOfIEnumerator;
238 CollectionKind kind = collectionContract.Kind;
239 if (kind == CollectionKind.GenericDictionary || kind == CollectionKind.GenericCollection || kind == CollectionKind.GenericEnumerable)
241 Type[] interfaceTypes = enumeratorType.GetInterfaces();
242 foreach (Type interfaceType in interfaceTypes)
244 if (interfaceType.IsGenericType
245 && interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
246 && interfaceType.GetGenericArguments()[0] == collectionContract.ItemType)
248 ienumeratorInterface = interfaceType;
253 if (moveNextMethod == null)
254 moveNextMethod = CollectionDataContract.GetTargetMethodWithName(Globals.MoveNextMethodName, enumeratorType, ienumeratorInterface);
255 if (getCurrentMethod == null)
256 getCurrentMethod = CollectionDataContract.GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface);
259 Type elementType = getCurrentMethod.ReturnType;
260 object currentValue = null; // of elementType
262 var enumerator = (IEnumerator) collectionContract.GetEnumeratorMethod.Invoke (objLocal, new object [0]);
265 ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor (Globals.ScanAllMembers, null, new Type[] { Globals.TypeOfIDictionaryEnumerator }, null);
266 enumerator = (IEnumerator) dictEnumCtor.Invoke (new object [] {enumerator});
268 else if (isGenericDictionary)
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});
275 bool canWriteSimpleDictionary = isDictionary || isGenericDictionary;
277 bool writeSimpleDictionary = canWriteSimpleDictionary && context.UseSimpleDictionaryFormat;
278 PropertyInfo genericDictionaryKeyProperty = null, genericDictionaryValueProperty = null;
280 if (canWriteSimpleDictionary)
282 Type genericDictionaryKeyValueType = Globals.TypeOfKeyValue.MakeGenericType (keyValueTypes);
283 genericDictionaryKeyProperty = genericDictionaryKeyValueType.GetProperty (JsonGlobals.KeyString);
284 genericDictionaryValueProperty = genericDictionaryKeyValueType.GetProperty (JsonGlobals.ValueString);
287 if (writeSimpleDictionary) {
288 WriteObjectAttribute ();
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);
296 WriteStartElement (key, 0 /*nameIndex*/);
297 WriteValue (genericDictionaryValueProperty.PropertyType, value);
301 WriteArrayAttribute ();
303 var emptyArray = new object [0];
304 while (enumerator != null && enumerator.MoveNext ()) {
305 currentValue = getCurrentMethod.Invoke (enumerator, emptyArray);
307 if (incrementCollectionCountMethod == null)
308 XmlFormatGeneratorStatics.IncrementItemCountMethod.Invoke (context, new object [] {1});
310 if (!TryWritePrimitive (elementType, () => currentValue, null, null, itemName, 0))
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);
320 WriteValue (elementType, currentValue);
328 int WriteMembers (ClassDataContract classContract, ExtensionDataObject extensionData, ClassDataContract derivedMostClassContract)
330 int memberCount = (classContract.BaseContract == null) ? 0 : WriteMembers (classContract.BaseContract, extensionData, derivedMostClassContract);
332 context.IncrementItemCount (classContract.Members.Count);
334 for (int i = 0; i < classContract.Members.Count; i++, memberCount++) {
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)
344 hasMemberValue = true;
345 memberValue = LoadMemberValue (member);
346 doWrite = !IsDefaultValue (memberType, memberValue);
351 bool requiresNameAttribute = DataContractJsonSerializer.CheckIfXmlNameRequiresMapping (classContract.MemberNames [i]);
353 if (requiresNameAttribute || !TryWritePrimitive(memberType, hasMemberValue ? () => memberValue : (Func<object>) null, member.MemberInfo, null /*arrayItemIndex*/, null /*nameLocal*/, i + childElementIndex)) {
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);
360 WriteStartElement (null /*nameLocal*/, i + childElementIndex);
362 if (memberValue == null)
363 memberValue = LoadMemberValue (member);
364 WriteValue (memberType, memberValue);
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);
377 childElementIndex += classContract.Members.Count;
382 internal bool IsDefaultValue (Type type, object value)
384 var def = GetDefaultValue (type);
385 return def == null ? (object) value == null : def.Equals (value);
388 internal object GetDefaultValue(Type type)
390 if (type.IsValueType)
392 switch (Type.GetTypeCode(type))
394 case TypeCode.Boolean:
400 case TypeCode.UInt16:
402 case TypeCode.UInt32:
405 case TypeCode.UInt64:
407 case TypeCode.Single:
409 case TypeCode.Double:
411 case TypeCode.Decimal:
412 return default (decimal);
413 case TypeCode.DateTime:
414 return default (DateTime);
420 void WriteStartElement (object nameLocal, int nameIndex)
422 var name = nameLocal ?? memberNames [nameIndex];
423 XmlDictionaryString namespaceLocal = null;
424 if (nameLocal != null && nameLocal is string)
425 writer.WriteStartElement ((string) name, null);
427 writer.WriteStartElement ((XmlDictionaryString) name, null);
430 void WriteEndElement ()
432 writer.WriteEndElement ();
435 void WriteArrayAttribute ()
437 writer.WriteAttributeString (
439 JsonGlobals.typeString /* local name */,
440 string.Empty /* namespace */,
441 JsonGlobals.arrayString /* value */);
444 void WriteObjectAttribute ()
446 writer.WriteAttributeString (
448 JsonGlobals.typeString /* local name */,
449 null /* namespace */,
450 JsonGlobals.objectString /* value */);
453 void WriteValue (Type memberType, object memberValue)
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)
462 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
463 if (primitiveContract != null)
464 primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
466 InternalSerialize (XmlFormatGeneratorStatics.InternalSerializeMethod, () => memberValue, memberType, false);
472 memberValue = UnwrapNullableObject(() => memberValue, ref memberType, out isNull); //Leaves !HasValue on stack
474 isNull = memberValue == null;
476 XmlFormatGeneratorStatics.WriteNullMethod.Invoke (context, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
478 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
479 if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
481 primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
483 primitiveContract.XmlFormatContentWriterMethod.Invoke (context, new object [] {writer, memberValue});
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;
494 XmlFormatGeneratorStatics.WriteNullMethod.Invoke (context, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
496 InternalSerialize((isNullableOfT ? XmlFormatGeneratorStatics.InternalSerializeMethod : XmlFormatGeneratorStatics.InternalSerializeReferenceMethod),
497 () => memberValue, memberType, false);
504 void InternalSerialize (MethodInfo methodInfo, Func<object> memberValue, Type memberType, bool writeXsiType)
506 var v = memberValue ();
507 var typeHandleValue = Type.GetTypeHandle (v);
508 var isDeclaredType = typeHandleValue.Equals (CodeInterpreter.ConvertValue (v, memberType, Globals.TypeOfObject));
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;
519 object UnwrapNullableObject(Func<object> memberValue, ref Type memberType, out bool isNull)// Leaves !HasValue on stack
521 object v = memberValue ();
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});
529 v = XmlFormatGeneratorStatics.GetDefaultValueMethod.MakeGenericMethod (memberType).Invoke (null, new object [0]);
531 memberType = innerType;
537 bool TryWritePrimitive(Type type, Func<object> value, MemberInfo memberInfo, int? arrayItemIndex, XmlDictionaryString name, int nameIndex)
539 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
540 if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
543 object callee = null;
544 var args = new List<object> ();
547 if (type.IsValueType)
553 // load primitive value
556 else if (memberInfo != null)
557 args.Add (CodeInterpreter.GetMember (memberInfo, objLocal));
559 args.Add (((Array) objLocal).GetValue (new int [] {(int) arrayItemIndex}));
564 args.Add (memberNames [nameIndex]);
567 // call method to write primitive
568 primitiveContract.XmlFormatWriterMethod.Invoke (callee, args.ToArray ());
572 bool TryWritePrimitiveArray (Type type, Type itemType, Func<object> value, XmlDictionaryString itemName)
574 PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
575 if (primitiveContract == null)
578 string writeArrayMethod = null;
579 switch (Type.GetTypeCode(itemType))
581 case TypeCode.Boolean:
582 writeArrayMethod = "WriteJsonBooleanArray";
584 case TypeCode.DateTime:
585 writeArrayMethod = "WriteJsonDateTimeArray";
587 case TypeCode.Decimal:
588 writeArrayMethod = "WriteJsonDecimalArray";
591 writeArrayMethod = "WriteJsonInt32Array";
594 writeArrayMethod = "WriteJsonInt64Array";
596 case TypeCode.Single:
597 writeArrayMethod = "WriteJsonSingleArray";
599 case TypeCode.Double:
600 writeArrayMethod = "WriteJsonDoubleArray";
605 if (writeArrayMethod != null)
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});
614 object LoadMemberValue (DataMember member)
616 return CodeInterpreter.GetMember (member.MemberInfo, objLocal);