Ver código fonte

[S.R.Serialization] add Json reader/writer interpreters.

They don't pass all the tests in System.ServiceModel.Web yet.
Atsushi Eno 10 anos atrás
pai
commit
cda40030b2

+ 0 - 1
mcs/class/System.Runtime.Serialization/ReferenceSource.common.sources

@@ -1,7 +1,6 @@
 
 ../../../external/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/AppSettings.cs
 ../../../external/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/Attributes.cs
-../../../external/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/BitFlagsGenerator.cs
 ../../../external/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/ClassDataContract.cs
 ../../../external/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/CodeGenerator.cs
 ../../../external/referencesource/System.Runtime.Serialization/System/Runtime/Serialization/CollectionDataContractAttribute.cs

+ 76 - 0
mcs/class/System.Runtime.Serialization/ReferenceSources/BitFlagsGenerator.cs

@@ -0,0 +1,76 @@
+using System;
+
+namespace System.Runtime.Serialization
+{
+	public class BitFlagsGenerator
+	{
+		int bitCount;
+		byte [] locals;
+		
+		public BitFlagsGenerator (int bitCount)
+		{
+			this.bitCount = bitCount;
+			int localCount = (bitCount+7)/8;
+			locals = new byte [localCount];
+		}
+		
+		public void Store (int bitIndex, bool value)
+		{
+			if (value)
+				locals [GetByteIndex (bitIndex)] |= GetBitValue(bitIndex);
+			else
+				locals [GetByteIndex (bitIndex)] &= (byte) ~GetBitValue(bitIndex);
+		}
+		
+		public bool Load (int bitIndex)
+		{
+			var local = locals[GetByteIndex(bitIndex)];
+			byte bitValue = GetBitValue(bitIndex);
+			return (local & bitValue) == bitValue;
+		}
+		
+		public byte [] LoadArray ()
+		{
+			return (byte []) locals.Clone ();
+		}
+		
+		public int GetLocalCount ()
+		{
+			return locals.Length;
+		}
+		
+		public int GetBitCount ()
+		{
+			return bitCount;
+		}
+		
+		public byte GetLocal (int i)
+		{
+			return locals [i];
+		}
+		
+		public static bool IsBitSet (byte[] bytes, int bitIndex)
+		{
+			int byteIndex = GetByteIndex (bitIndex);
+			byte bitValue = GetBitValue (bitIndex);
+			return (bytes[byteIndex] & bitValue) == bitValue;
+		}
+
+		public static void SetBit (byte[] bytes, int bitIndex)
+		{
+			int byteIndex = GetByteIndex (bitIndex);
+			byte bitValue = GetBitValue (bitIndex);
+			bytes[byteIndex] |= bitValue;
+		}
+
+		static int GetByteIndex (int bitIndex)
+		{
+			return bitIndex >> 3;
+		}
+		
+		static byte GetBitValue (int bitIndex)
+		{
+			return (byte)(1 << (bitIndex & 7));
+		}
+	}
+}

+ 680 - 7
mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatReaderGenerator_static.cs

@@ -2,6 +2,8 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
+using System.Runtime;
+using System.Xml;
 
 namespace System.Runtime.Serialization.Json
 {
@@ -9,19 +11,690 @@ namespace System.Runtime.Serialization.Json
 	{
 		partial class CriticalHelper
 		{
-			public JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
+			internal JsonFormatClassReaderDelegate GenerateClassReader(ClassDataContract classContract)
 			{
-				throw new NotImplementedException ();
+				return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDictionaryString, XmlDictionaryString [] memberNames) => new JsonFormatReaderInterpreter (classContract).ReadFromJson (xr, ctx, emptyDictionaryString, memberNames);
 			}
-			public JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
+
+			internal JsonFormatCollectionReaderDelegate GenerateCollectionReader(CollectionDataContract collectionContract)
 			{
-				throw new NotImplementedException ();
+				return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDS, XmlDictionaryString inm, CollectionDataContract cc) => new JsonFormatReaderInterpreter (collectionContract, false).ReadCollectionFromJson (xr, ctx, emptyDS, inm, cc);
 			}
-			public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
+			
+			internal JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader(CollectionDataContract collectionContract)
 			{
-				throw new NotImplementedException ();
+				return (XmlReaderDelegator xr, XmlObjectSerializerReadContextComplexJson ctx, XmlDictionaryString emptyDS, XmlDictionaryString inm, CollectionDataContract cc) => new JsonFormatReaderInterpreter (collectionContract, true).ReadGetOnlyCollectionFromJson (xr, ctx, emptyDS, inm, cc);
 			}
 		}
 	}
-}
 
+	class JsonFormatReaderInterpreter
+	{
+		public JsonFormatReaderInterpreter (ClassDataContract classContract)
+		{
+			this.classContract = classContract;
+		}
+
+		public JsonFormatReaderInterpreter (CollectionDataContract collectionContract, bool isGetOnly)
+		{
+			this.collectionContract = collectionContract;
+			this.is_get_only_collection = isGetOnly;
+		}
+
+		bool is_get_only_collection;
+
+		ClassDataContract classContract;
+
+		CollectionDataContract collectionContract;
+
+		object objectLocal;
+		Type objectType;
+		XmlReaderDelegator xmlReader;
+		XmlObjectSerializerReadContextComplexJson context;
+
+		XmlDictionaryString [] memberNames = null;
+		XmlDictionaryString emptyDictionaryString = null;
+		XmlDictionaryString itemName = null;
+		XmlDictionaryString itemNamespace = null;
+
+		public object ReadFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString[] memberNames)
+		{
+			// InitArgs()
+			this.xmlReader = xmlReader;
+			this.context = context;
+			this.emptyDictionaryString = emptyDictionaryString;
+			this.memberNames = memberNames;
+			
+			//DemandSerializationFormatterPermission(classContract);
+			//DemandMemberAccessPermission(memberAccessFlag);
+			CreateObject (classContract);
+			
+			context.AddNewObject (objectLocal);
+			InvokeOnDeserializing (classContract);
+            
+            string objectId = null;
+            
+			if (classContract.IsISerializable)
+				ReadISerializable (classContract);
+			else
+				ReadClass (classContract);
+			if (Globals.TypeOfIDeserializationCallback.IsAssignableFrom (classContract.UnderlyingType))
+				((IDeserializationCallback) objectLocal).OnDeserialization (null);
+			InvokeOnDeserialized(classContract);
+			if (!InvokeFactoryMethod (classContract)) {
+
+				// Do a conversion back from DateTimeOffsetAdapter to DateTimeOffset after deserialization.
+				// DateTimeOffsetAdapter is used here for deserialization purposes to bypass the ISerializable implementation
+				// on DateTimeOffset; which does not work in partial trust.
+
+				if (classContract.UnderlyingType == Globals.TypeOfDateTimeOffsetAdapter)
+					objectLocal = DateTimeOffsetAdapter.GetDateTimeOffset ((DateTimeOffsetAdapter) objectLocal);
+				// else - do we have to call CodeInterpreter.ConvertValue()? I guess not...
+			}
+			return objectLocal;
+		}
+		
+		public object ReadCollectionFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract)
+		{
+			#region GenerateCollectionReaderHelper
+			// InitArgs()
+			this.xmlReader = xmlReader;
+			this.context = context;
+			this.emptyDictionaryString = emptyDictionaryString;
+			this.itemName = itemName;
+
+			this.collectionContract = collectionContract;
+
+			#endregion
+
+			ReadCollection (collectionContract);
+
+			return objectLocal;
+		}
+		
+		public void ReadGetOnlyCollectionFromJson (XmlReaderDelegator xmlReader, XmlObjectSerializerReadContextComplexJson context, XmlDictionaryString emptyDictionaryString, XmlDictionaryString itemName, CollectionDataContract collectionContract)
+		{
+			#region GenerateCollectionReaderHelper
+			// InitArgs()
+			this.xmlReader = xmlReader;
+			this.context = context;
+			this.emptyDictionaryString = emptyDictionaryString;
+			this.itemName = itemName;
+
+			this.collectionContract = collectionContract;
+
+			#endregion
+
+			ReadGetOnlyCollection (collectionContract);
+		}
+
+		void CreateObject (ClassDataContract classContract)
+		{
+			Type type = objectType = classContract.UnderlyingType;
+			if (type.IsValueType && !classContract.IsNonAttributedType)
+				type = Globals.TypeOfValueType;
+
+			if (classContract.UnderlyingType == Globals.TypeOfDBNull)
+				objectLocal = DBNull.Value;
+			else if (classContract.IsNonAttributedType) {
+				if (type.IsValueType)
+					objectLocal = FormatterServices.GetUninitializedObject (type);
+				else
+					objectLocal = classContract.GetNonAttributedTypeConstructor ().Invoke (new object [0]);
+			}
+			else
+				objectLocal = CodeInterpreter.ConvertValue (XmlFormatReaderGenerator.UnsafeGetUninitializedObject (DataContract.GetIdForInitialization (classContract)), Globals.TypeOfObject, type);
+		}
+
+		void InvokeOnDeserializing (ClassDataContract classContract)
+		{
+			if (classContract.BaseContract != null)
+				InvokeOnDeserializing (classContract.BaseContract);
+			if (classContract.OnDeserializing != null)
+				classContract.OnDeserializing.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
+		}
+
+		void InvokeOnDeserialized (ClassDataContract classContract)
+		{
+			if (classContract.BaseContract != null)
+				InvokeOnDeserialized (classContract.BaseContract);
+			if (classContract.OnDeserialized != null)
+				classContract.OnDeserialized.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
+		}
+
+		bool HasFactoryMethod (ClassDataContract classContract)
+		{
+			return Globals.TypeOfIObjectReference.IsAssignableFrom (classContract.UnderlyingType);
+		}
+
+		bool InvokeFactoryMethod (ClassDataContract classContract)
+		{
+			if (HasFactoryMethod (classContract)) {
+				objectLocal = CodeInterpreter.ConvertValue (context.GetRealObject ((IObjectReference) objectLocal, Globals.NewObjectId), Globals.TypeOfObject, classContract.UnderlyingType);
+				return true;
+			}
+			return false;
+		}
+
+		void ReadISerializable (ClassDataContract classContract)
+		{
+			ConstructorInfo ctor = classContract.UnderlyingType.GetConstructor (Globals.ScanAllMembers, null, JsonFormatGeneratorStatics.SerInfoCtorArgs, null);
+			if (ctor == null)
+				throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError (XmlObjectSerializer.CreateSerializationException (SR.GetString (SR.SerializationInfo_ConstructorNotFound, DataContract.GetClrTypeFullName (classContract.UnderlyingType))));
+			context.ReadSerializationInfo (xmlReader, classContract.UnderlyingType);
+			ctor.Invoke (objectLocal, new object [] {context.GetStreamingContext ()});
+		}
+
+		void ReadClass (ClassDataContract classContract)
+		{
+			if (classContract.HasExtensionData) {
+				ExtensionDataObject extensionData = new ExtensionDataObject ();
+				ReadMembers (classContract, extensionData);
+				ClassDataContract currentContract = classContract;
+				while (currentContract != null) {
+					MethodInfo extensionDataSetMethod = currentContract.ExtensionDataSetMethod;
+					if (extensionDataSetMethod != null)
+						extensionDataSetMethod.Invoke (objectLocal, new object [] {extensionData});
+					currentContract = currentContract.BaseContract;
+				}
+			}
+			else
+				ReadMembers (classContract, null);
+		}
+
+		void ReadMembers (ClassDataContract classContract, ExtensionDataObject  extensionData)
+		{
+			int memberCount = classContract.MemberNames.Length;
+			context.IncrementItemCount (memberCount);
+
+			int memberIndex = -1;
+			
+			// JSON intrinsic part.
+			BitFlagsGenerator expectedElements = new BitFlagsGenerator (memberCount);
+			byte [] requiredElements = new byte [expectedElements.GetLocalCount ()];
+			SetRequiredElements (classContract, requiredElements);
+			SetExpectedElements (expectedElements, 0 /*startIndex*/);
+
+			while (XmlObjectSerializerReadContext.MoveToNextElement (xmlReader)) {
+				int idx; // used as in "switch (idx)" in the original source.
+				idx = context.GetJsonMemberIndex (xmlReader, memberNames, memberIndex, extensionData);
+
+				if (memberCount > 0)
+					ReadMembers (idx, classContract, expectedElements, ref memberIndex);
+			}
+
+			if (!CheckRequiredElements (expectedElements, requiredElements))
+				XmlObjectSerializerReadContextComplexJson.ThrowMissingRequiredMembers (objectLocal, memberNames, expectedElements.LoadArray (), requiredElements);
+		}
+
+		int ReadMembers (int index, ClassDataContract classContract, BitFlagsGenerator expectedElements, ref int memberIndex)
+		{
+			int memberCount = (classContract.BaseContract == null) ? 0 : ReadMembers (index, classContract.BaseContract, expectedElements,
+			ref memberIndex);
+			
+			if (memberCount <= index && index < memberCount + classContract.Members.Count) {
+				DataMember dataMember = classContract.Members [index - memberCount];
+				Type memberType = dataMember.MemberType;
+				
+				memberIndex = memberCount;
+				if (!expectedElements.Load (memberCount))
+					XmlObjectSerializerReadContextComplexJson.ThrowDuplicateMemberException (objectLocal, memberNames, memberIndex);
+
+				if (dataMember.IsGetOnlyCollection) {
+					var value = CodeInterpreter.GetMember (dataMember.MemberInfo, objectLocal);
+					context.StoreCollectionMemberInfo (value);
+					ReadValue (memberType, dataMember.Name);
+				} else {
+					var value = ReadValue (memberType, dataMember.Name);
+					CodeInterpreter.SetMember (dataMember.MemberInfo, objectLocal, value);
+				}
+				memberIndex = index;
+				ResetExpectedElements(expectedElements, index);
+			}
+			return memberCount + classContract.Members.Count;
+		}
+
+		bool CheckRequiredElements (BitFlagsGenerator expectedElements, byte [] requiredElements)
+		{
+			for (int i = 0; i < requiredElements.Length; i++)
+				if ((expectedElements.GetLocal(i) & requiredElements[i]) != 0)
+					return false;
+			return true;
+		}
+
+		int SetRequiredElements (ClassDataContract contract, byte [] requiredElements)
+		{
+			int memberCount = (contract.BaseContract == null) ? 0 :
+			SetRequiredElements (contract.BaseContract, requiredElements);
+			List<DataMember> members = contract.Members;
+			for (int i = 0; i < members.Count; i++, memberCount++) {
+				if (members[i].IsRequired)
+					BitFlagsGenerator.SetBit (requiredElements, memberCount);
+			}
+			return memberCount;
+		}
+
+		void SetExpectedElements (BitFlagsGenerator expectedElements, int startIndex)
+		{
+			int memberCount = expectedElements.GetBitCount ();
+			for (int i = startIndex; i < memberCount; i++)
+				expectedElements.Store (i, true);
+		}
+
+		void ResetExpectedElements (BitFlagsGenerator expectedElements, int index)
+		{
+			expectedElements.Store (index, false);
+		}
+
+		object ReadValue (Type type, string name)
+		{
+			var valueType = type;
+			object value = null;
+			bool shouldAssignNullableValue = false;
+			int nullables = 0;
+			while (type.IsGenericType && type.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
+				nullables++;
+				type = type.GetGenericArguments () [0];
+			}
+			
+			PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (type);
+			if ((primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) || nullables != 0 || type.IsValueType) {
+				context.ReadAttributes (xmlReader);
+				string objectId = context.ReadIfNullOrRef (xmlReader, type, DataContract.IsTypeSerializable (type));
+				// Deserialize null
+                if (objectId == Globals.NullObjectId) {
+					
+					if (nullables != 0)
+						value = Activator.CreateInstance (valueType);
+					else if (type.IsValueType)
+						throw new SerializationException (SR.GetString (SR.ValueTypeCannotBeNull, DataContract.GetClrTypeFullName (type)));
+					else
+						value = null;
+				} else if (objectId == string.Empty) {
+					// Deserialize value
+
+					// Compare against Globals.NewObjectId, which is set to string.Empty
+					
+					objectId = context.GetObjectId ();
+					
+					if (type.IsValueType) {
+						if (!string.IsNullOrEmpty (objectId))
+							throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveId, DataContract.GetClrTypeFullName(type)));
+					}
+					object innerValueRead = null;
+					if (nullables != 0)
+						shouldAssignNullableValue = true;
+
+					if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
+						value = primitiveContract.XmlFormatReaderMethod.Invoke (xmlReader, new object [0]);
+						if (!type.IsValueType)
+							context.AddNewObject (value);
+					}
+					else
+							value = InternalDeserialize (type, name);
+				} else {
+					// Deserialize ref
+					if (type.IsValueType)
+						throw new SerializationException (SR.GetString (SR.ValueTypeCannotHaveRef, DataContract.GetClrTypeFullName (type)));
+					else
+						value = CodeInterpreter.ConvertValue (context.GetExistingObject (objectId, type, name, string.Empty), Globals.TypeOfObject, type);
+				}
+
+				if (shouldAssignNullableValue) {
+					if (objectId != Globals.NullObjectId)
+						value = WrapNullableObject (type, value, valueType, nullables);
+				}
+			}
+			else
+				value = InternalDeserialize (type, name);
+
+			return value;
+		}
+
+		object InternalDeserialize (Type type, string name)
+		{
+			Type declaredType = type.IsPointer ? Globals.TypeOfReflectionPointer : type;
+			var obj = context.InternalDeserialize (xmlReader, DataContract.GetId (declaredType.TypeHandle), declaredType.TypeHandle, name, string.Empty);
+
+			if (type.IsPointer)
+				// wow, there is no way to convert void* to object in strongly typed way...
+				return JsonFormatGeneratorStatics.UnboxPointer.Invoke (null, new object [] {obj});
+			else
+				return CodeInterpreter.ConvertValue (obj, Globals.TypeOfObject, type);
+		}
+
+		object WrapNullableObject (Type innerType, object innerValue, Type outerType, int nullables)
+		{
+			var outerValue = innerValue;
+			for (int i = 1; i < nullables; i++) {
+				Type type = Globals.TypeOfNullable.MakeGenericType (innerType);
+				outerValue = Activator.CreateInstance (type, new object[] { outerValue });
+				innerType = type;
+			}
+			return Activator.CreateInstance (outerType, new object[] { outerValue });
+		}
+
+
+		void ReadCollection (CollectionDataContract collectionContract)
+		{
+			Type type = collectionContract.UnderlyingType;
+			Type itemType = collectionContract.ItemType;
+			bool isArray = (collectionContract.Kind == CollectionKind.Array);
+
+			ConstructorInfo constructor = collectionContract.Constructor;
+
+			if (type.IsInterface) {
+				switch (collectionContract.Kind) {
+				case CollectionKind.GenericDictionary:
+					type = Globals.TypeOfDictionaryGeneric.MakeGenericType (itemType.GetGenericArguments ());
+					constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
+					break;
+				case CollectionKind.Dictionary:
+					type = Globals.TypeOfHashtable;
+					constructor = type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
+					break;
+				case CollectionKind.Collection:
+				case CollectionKind.GenericCollection:
+				case CollectionKind.Enumerable:
+				case CollectionKind.GenericEnumerable:
+				case CollectionKind.List:
+				case CollectionKind.GenericList:
+					type = itemType.MakeArrayType ();
+					isArray = true;
+					break;
+				}
+			}
+
+			if (!isArray) {
+				if (type.IsValueType)
+					// FIXME: this is not what the original code does.
+					objectLocal = FormatterServices.GetUninitializedObject (type);
+				else {
+					objectLocal = constructor.Invoke (new object [0]);
+					context.AddNewObject (objectLocal);
+				}
+			}
+
+			bool canReadSimpleDictionary = collectionContract.Kind == CollectionKind.Dictionary ||
+			collectionContract.Kind == CollectionKind.GenericDictionary;
+
+			if (canReadSimpleDictionary & context.UseSimpleDictionaryFormat)
+				ReadSimpleDictionary (collectionContract, itemType);
+			else {	 
+				string objectId = context.GetObjectId ();
+
+				bool canReadPrimitiveArray = false, readResult = false;
+				if (isArray && TryReadPrimitiveArray (itemType, out readResult))
+					canReadPrimitiveArray = true;
+
+				if (!readResult) {
+					object growingCollection = null;
+					if (isArray)
+						growingCollection = Array.CreateInstance (itemType, 32);
+
+					int i = 0;
+					// FIXME: I cannot find i++ part, but without that it won't work as expected.
+					for (; i < int.MaxValue; i++) {
+						if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
+							context.IncrementItemCount (1);
+							object value = ReadCollectionItem (collectionContract, itemType);
+							if (isArray) {
+								MethodInfo ensureArraySizeMethod = XmlFormatGeneratorStatics.EnsureArraySizeMethod.MakeGenericMethod (itemType);
+								growingCollection = ensureArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
+								((Array) growingCollection).SetValue (value, i);
+							} else {
+								StoreCollectionValue (objectLocal, itemType, value, collectionContract);
+							}
+						}
+						else if (IsEndElement ())
+							break;
+						else
+							HandleUnexpectedItemInCollection (ref i);
+					}
+
+					if (isArray) {
+						MethodInfo trimArraySizeMethod = XmlFormatGeneratorStatics.TrimArraySizeMethod.MakeGenericMethod (itemType);
+						objectLocal = trimArraySizeMethod.Invoke (null, new object [] {growingCollection, i});
+						context.AddNewObjectWithId (objectId, objectLocal);
+					}
+				}
+				if (canReadPrimitiveArray)
+					context.AddNewObjectWithId (objectId, objectLocal);
+			}
+		}
+
+		void ReadSimpleDictionary (CollectionDataContract collectionContract, Type keyValueType)
+		{
+			Type[] keyValueTypes = keyValueType.GetGenericArguments ();
+			Type keyType = keyValueTypes [0];
+			Type valueType = keyValueTypes [1];
+
+			int keyTypeNullableDepth = 0;
+			Type keyTypeOriginal = keyType;
+			while (keyType.IsGenericType && keyType.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
+				keyTypeNullableDepth++;
+				keyType = keyType.GetGenericArguments () [0];
+			}
+
+			ClassDataContract keyValueDataContract = (ClassDataContract)collectionContract.ItemContract;
+			DataContract keyDataContract = keyValueDataContract.Members [0].MemberTypeContract;
+
+			KeyParseMode keyParseMode = KeyParseMode.Fail;
+
+			if (keyType == Globals.TypeOfString || keyType == Globals.TypeOfObject) {
+				keyParseMode = KeyParseMode.AsString;
+			} else if (keyType.IsEnum) {
+				keyParseMode = KeyParseMode.UsingParseEnum;
+			} else if (keyDataContract.ParseMethod != null) {
+				keyParseMode = KeyParseMode.UsingCustomParse;
+			}
+
+			if (keyParseMode == KeyParseMode.Fail) {
+				ThrowSerializationException (
+				SR.GetString (
+				SR.KeyTypeCannotBeParsedInSimpleDictionary,
+				DataContract.GetClrTypeFullName (collectionContract.UnderlyingType),
+				DataContract.GetClrTypeFullName (keyType)));
+			} else {
+				XmlNodeType nodeType;
+
+				while ((nodeType = xmlReader.MoveToContent ()) != XmlNodeType.EndElement) {
+					if (nodeType != XmlNodeType.Element)
+						ThrowUnexpectedStateException (XmlNodeType.Element);
+
+					context.IncrementItemCount (1);
+
+					var jsonMemberName = XmlObjectSerializerReadContextComplexJson.GetJsonMemberName (xmlReader);
+					object key = null;
+
+					if (keyParseMode == KeyParseMode.UsingParseEnum)
+						key = Enum.Parse (keyType, jsonMemberName);
+					else if (keyParseMode == KeyParseMode.UsingCustomParse)
+						key = keyDataContract.ParseMethod.Invoke (null, new object [] {jsonMemberName});
+
+					if (keyTypeNullableDepth > 0) {
+						var keyOriginal = WrapNullableObject (keyType, key, valueType, keyTypeNullableDepth);
+						key = keyOriginal;
+					}
+
+					var value = ReadValue (valueType, String.Empty);
+					collectionContract.AddMethod.Invoke (objectLocal, new object[] {key, value});
+				}
+			}
+		}
+
+			// CONTINUE FROM HERE
+
+		void ReadGetOnlyCollection (CollectionDataContract collectionContract)
+		{
+			Type type = collectionContract.UnderlyingType;
+			Type itemType = collectionContract.ItemType;
+			bool isArray = (collectionContract.Kind == CollectionKind.Array);
+			int size = 0;
+
+			objectLocal = context.GetCollectionMember ();
+ 
+			bool canReadSimpleDictionary = 
+				collectionContract.Kind == CollectionKind.Dictionary ||
+				collectionContract.Kind == CollectionKind.GenericDictionary;
+
+			bool readSimple = canReadSimpleDictionary && context.UseSimpleDictionaryFormat;
+			if (readSimple) {
+				if (objectLocal == null)
+					XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
+				else {
+					ReadSimpleDictionary(collectionContract, itemType);
+					context.CheckEndOfArray (xmlReader, size, this.itemName, emptyDictionaryString);
+				}
+			} else {
+
+				//check that items are actually going to be deserialized into the collection
+				if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
+					if (objectLocal == null)
+						XmlObjectSerializerReadContext.ThrowNullValueReturnedForGetOnlyCollectionException (type);
+					else {
+						size = 0;
+						if (isArray)
+							size = ((Array) objectLocal).Length;
+						for (int i = 0; i < int.MaxValue;) {
+							if (IsStartElement (this.itemName, this.emptyDictionaryString)) {
+								context.IncrementItemCount (1);
+								var value = ReadCollectionItem (collectionContract, itemType);
+								if (isArray) {
+									if (size == i)
+										XmlObjectSerializerReadContext.ThrowArrayExceededSizeException (size, type);
+									else
+										((Array) objectLocal).SetValue (value, i);
+								} else {
+									StoreCollectionValue (objectLocal, itemType, value, collectionContract);
+								}
+							}
+							else if (IsEndElement())
+								break;
+							else
+								HandleUnexpectedItemInCollection (ref i);
+						}
+						context.CheckEndOfArray (xmlReader, size, this.itemName, this.emptyDictionaryString);
+					}
+				}
+			}
+		}
+
+		bool TryReadPrimitiveArray (Type itemType, out bool readResult)
+		{
+			readResult = false;
+			PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract (itemType);
+			if (primitiveContract == null)
+				return false;
+
+			string readArrayMethod = null;
+			switch (Type.GetTypeCode (itemType))
+			{
+			case TypeCode.Boolean:
+				readArrayMethod = "TryReadBooleanArray";
+			break;
+			case TypeCode.Decimal:
+				readArrayMethod = "TryReadDecimalArray";
+			break;
+			case TypeCode.Int32:
+				readArrayMethod = "TryReadInt32Array";
+			break;
+			case TypeCode.Int64:
+				readArrayMethod = "TryReadInt64Array";
+			break;
+			case TypeCode.Single:
+				readArrayMethod = "TryReadSingleArray";
+			break;
+			case TypeCode.Double:
+				readArrayMethod = "TryReadDoubleArray";
+				break;
+			case TypeCode.DateTime:
+				readArrayMethod = "TryReadJsonDateTimeArray";
+			break;
+			default:
+				break;
+			}
+			if (readArrayMethod != null) {
+				var mi = typeof (JsonReaderDelegator).GetMethod (readArrayMethod, Globals.ScanAllMembers);
+				var args = new object [] {context, itemName, emptyDictionaryString, -1, objectLocal};
+				readResult = (bool) mi.Invoke ((JsonReaderDelegator) xmlReader, args);
+				objectLocal = args.Last ();
+				return true;
+			}
+			return false;
+		}
+
+		object ReadCollectionItem (CollectionDataContract collectionContract, Type itemType)
+		{
+			if (collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary) {
+				context.ResetAttributes ();
+				var v = DataContractJsonSerializer.ReadJsonValue (XmlObjectSerializerWriteContextComplexJson.GetRevisedItemContract (collectionContract.ItemContract), xmlReader, context);
+				return CodeInterpreter.ConvertValue (v, Globals.TypeOfObject, itemType);
+			}
+			else
+				return ReadValue (itemType, JsonGlobals.itemString);
+		}
+
+		void StoreCollectionValue (object collection, Type valueType, object value, CollectionDataContract collectionContract)
+		{
+			if (collectionContract.Kind == CollectionKind.GenericDictionary || collectionContract.Kind == CollectionKind.Dictionary) {
+				ClassDataContract keyValuePairContract = DataContract.GetDataContract (valueType) as ClassDataContract;
+				if (keyValuePairContract == null)
+					Fx.Assert ("Failed to create contract for KeyValuePair type");
+				DataMember keyMember = keyValuePairContract.Members [0];
+				DataMember valueMember = keyValuePairContract.Members [1];
+				object pkey = CodeInterpreter.GetMember (keyMember.MemberInfo, value);
+				object pvalue = CodeInterpreter.GetMember (valueMember.MemberInfo, value);
+				
+				collectionContract.AddMethod.Invoke (collection, new object [] {pkey, pvalue});
+			}
+			else
+				collectionContract.AddMethod.Invoke (collection, new object [] {value});
+		}
+
+		void HandleUnexpectedItemInCollection (ref int iterator)
+		{
+			if (IsStartElement ()) {
+				context.SkipUnknownElement (xmlReader);
+				iterator--;
+			}
+			else 
+				throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (XmlNodeType.Element, xmlReader);
+		}
+
+		bool IsStartElement(XmlDictionaryString name, XmlDictionaryString ns)
+		{
+			return xmlReader.IsStartElement (name, ns);
+		}
+
+		bool IsStartElement()
+		{
+			return xmlReader.IsStartElement ();
+		}
+
+		bool IsEndElement ()
+		{
+			return xmlReader.NodeType == XmlNodeType.EndElement;
+		}
+
+		void ThrowUnexpectedStateException (XmlNodeType expectedState)
+		{
+			throw XmlObjectSerializerReadContext.CreateUnexpectedStateException (expectedState, xmlReader);
+		}
+
+		void ThrowSerializationException (string msg, params object [] values)
+		{
+			if (values != null && values.Length > 0)
+				msg = string.Format (msg, values);
+			throw new SerializationException (msg);
+		}
+
+		enum KeyParseMode
+		{
+			Fail,
+			AsString,
+			UsingParseEnum,
+			UsingCustomParse
+		}
+	}
+}

+ 590 - 2
mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatWriterGenerator_static.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
@@ -12,13 +13,600 @@ namespace System.Runtime.Serialization.Json
 		{
 			internal JsonFormatClassWriterDelegate GenerateClassWriter(ClassDataContract classContract)
 			{
-				throw new NotImplementedException ();
+				return (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString [] memberNames) => new JsonFormatWriterInterpreter (classContract).WriteToJson (xmlWriter, obj, context, dataContract, memberNames);
 			}
 			internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDataContract collectionContract)
 			{
-				throw new NotImplementedException ();
+				return (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract) => new JsonFormatWriterInterpreter (collectionContract).WriteCollectionToJson (xmlWriter, obj, context, dataContract);
 			}
 		}
 	}
+
+	class JsonFormatWriterInterpreter
+	{
+		public JsonFormatWriterInterpreter (ClassDataContract classContract)
+		{
+			this.classContract = classContract;
+		}
+
+		public JsonFormatWriterInterpreter (CollectionDataContract collectionContract)
+		{
+			this.collectionContract = collectionContract;
+		}
+
+		ClassDataContract classContract;
+
+		CollectionDataContract collectionContract;
+
+		XmlWriterDelegator writer = null;
+		object obj = null;
+		XmlObjectSerializerWriteContextComplexJson context = null;
+		DataContract dataContract = null;
+		object objLocal = null;
+
+		ClassDataContract classDataContract {
+			get { return (ClassDataContract) dataContract; }
+		}
+		CollectionDataContract collectionDataContract {
+			get {return (CollectionDataContract) dataContract; }
+		}
+
+		XmlDictionaryString [] memberNames = null;
+		int typeIndex = 1;
+		int childElementIndex = 0;
+
+		public void WriteToJson (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString [] memberNames)
+		{
+			this.writer = xmlWriter;
+			this.obj = obj;
+			this.context = context;
+			this.dataContract = dataContract;
+			this.memberNames = memberNames;
+
+			InitArgs (classContract.UnderlyingType);
+
+			// DemandSerializationFormatterPermission (classContract) - irrelevant
+			// DemandMemberAccessPermission (memberAccessFlag) - irrelevant
+
+			if (classContract.IsReadOnlyContract)
+			{
+				DataContract.ThrowInvalidDataContractException (classContract.SerializationExceptionMessage, null);
+			}
+
+			WriteClass (classContract);
+		}
+
+		public void WriteCollectionToJson (XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract)
+		{
+			this.writer = xmlWriter;
+			this.obj = obj;
+			this.context = context;
+			this.dataContract = dataContract;
+
+			InitArgs (collectionContract.UnderlyingType);			
+
+			// DemandMemberAccessPermission(memberAccessFlag);
+			if (collectionContract.IsReadOnlyContract)
+			{
+				DataContract.ThrowInvalidDataContractException (collectionContract.SerializationExceptionMessage, null);
+			}
+
+			WriteCollection (collectionContract);
+		}
+
+		void InitArgs (Type objType)
+		{
+			if (objType == Globals.TypeOfDateTimeOffsetAdapter) {
+				objLocal = DateTimeOffsetAdapter.GetDateTimeOffsetAdapter ((DateTimeOffset) obj);
+			}
+			else
+				objLocal = CodeInterpreter.ConvertValue (obj, typeof (object), objType);
+		}
+
+		void InvokeOnSerializing (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext context)
+		{
+			if (classContract.BaseContract != null)
+				InvokeOnSerializing (classContract.BaseContract, objSerialized, context);
+			if (classContract.OnSerializing != null) {
+				classContract.OnSerializing.Invoke (objSerialized, new object [] {context.GetStreamingContext ()});
+			}
+		}
+
+		void InvokeOnSerialized (ClassDataContract classContract, object objSerialized, XmlObjectSerializerWriteContext context)
+		{
+			if (classContract.BaseContract != null)
+				InvokeOnSerialized (classContract.BaseContract, objSerialized, context);
+			if (classContract.OnSerialized != null) {
+				classContract.OnSerialized.Invoke (objSerialized, new object [] {context.GetStreamingContext ()});
+			}
+		}
+
+		void WriteClass (ClassDataContract classContract)
+		{
+			InvokeOnSerializing (classContract, objLocal, context);
+
+			if (classContract.IsISerializable)
+				context.WriteJsonISerializable (writer, (ISerializable) objLocal);
+			else
+			{
+				if (classContract.HasExtensionData)
+				{
+					ExtensionDataObject extensionData = ((IExtensibleDataObject) objLocal).ExtensionData;
+					context.WriteExtensionData (writer, extensionData, -1);
+
+					WriteMembers (classContract, extensionData, classContract);
+				}
+				else
+					WriteMembers (classContract, null, classContract);
+			}
+			InvokeOnSerialized (classContract, objLocal, context);
+		}
+
+		void WriteCollection(CollectionDataContract collectionContract)
+		{
+			XmlDictionaryString itemName = context.CollectionItemName;
+
+			if (collectionContract.Kind == CollectionKind.Array)
+			{
+				Type itemType = collectionContract.ItemType;
+				int i;
+
+				// This check does not exist in the original dynamic code,
+				// but there is no other way to check type mismatch.
+				// CollectionSerialization.ArrayContract() shows that it is required.
+				if (objLocal.GetType ().GetElementType () != itemType)
+					throw new InvalidCastException (string.Format ("Cannot cast array of {0} to array of {1}", objLocal.GetType ().GetElementType (), itemType));
+
+				context.IncrementArrayCount (writer, (Array) objLocal);
+
+				if (!TryWritePrimitiveArray(collectionContract.UnderlyingType, itemType, () => objLocal, itemName))
+				{
+					WriteArrayAttribute ();
+					var arr = (Array) objLocal;
+					var idx = new int [1];
+					for (i = 0; i < arr.Length; i++) {
+						if (!TryWritePrimitive(itemType, null, null, i, itemName, 0)) {
+							WriteStartElement (itemName, 0);
+							idx [0] = i;
+							var mbrVal = arr.GetValue (idx);
+							WriteValue (itemType, mbrVal);
+							WriteEndElement ();
+						}
+					}
+				}
+			}
+			else
+			{
+				// This check does not exist in the original dynamic code,
+				// but there is no other way to check type mismatch.
+				// CollectionSerialization.ArrayContract() shows that it is required.
+				if (!collectionContract.UnderlyingType.IsAssignableFrom (objLocal.GetType ()))
+					throw new InvalidCastException (string.Format ("Cannot cast {0} to {1}", objLocal.GetType (), collectionContract.UnderlyingType));
+				
+				MethodInfo incrementCollectionCountMethod = null;
+				switch (collectionContract.Kind)
+				{
+				case CollectionKind.Collection:
+				case CollectionKind.List:
+				case CollectionKind.Dictionary:
+					incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountMethod;
+					break;
+				case CollectionKind.GenericCollection:
+				case CollectionKind.GenericList:
+					incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(collectionContract.ItemType);
+					break;
+				case CollectionKind.GenericDictionary:
+					incrementCollectionCountMethod = XmlFormatGeneratorStatics.IncrementCollectionCountGenericMethod.MakeGenericMethod(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
+					break;
+				}
+				if (incrementCollectionCountMethod != null)
+					incrementCollectionCountMethod.Invoke (context, new object [] {writer, objLocal});
+
+				bool isDictionary = false, isGenericDictionary = false;
+				Type enumeratorType = null;
+				Type [] keyValueTypes = null;
+				if (collectionContract.Kind == CollectionKind.GenericDictionary)
+				{
+					isGenericDictionary = true;
+					keyValueTypes = collectionContract.ItemType.GetGenericArguments ();
+					enumeratorType = Globals.TypeOfGenericDictionaryEnumerator.MakeGenericType (keyValueTypes);
+				}
+				else if (collectionContract.Kind == CollectionKind.Dictionary)
+				{
+					isDictionary = true;
+					keyValueTypes = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
+					enumeratorType = Globals.TypeOfDictionaryEnumerator;
+				}
+				else
+				{
+					enumeratorType = collectionContract.GetEnumeratorMethod.ReturnType;
+				}
+				MethodInfo moveNextMethod = enumeratorType.GetMethod (Globals.MoveNextMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
+				MethodInfo getCurrentMethod = enumeratorType.GetMethod (Globals.GetCurrentMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
+				if (moveNextMethod == null || getCurrentMethod == null)
+				{
+					if (enumeratorType.IsInterface)
+					{
+						if (moveNextMethod == null)
+							moveNextMethod = JsonFormatGeneratorStatics.MoveNextMethod;
+						if (getCurrentMethod == null)
+							getCurrentMethod = JsonFormatGeneratorStatics.GetCurrentMethod;
+					}
+					else
+					{
+						Type ienumeratorInterface = Globals.TypeOfIEnumerator;
+						CollectionKind kind = collectionContract.Kind;
+						if (kind == CollectionKind.GenericDictionary || kind == CollectionKind.GenericCollection || kind == CollectionKind.GenericEnumerable)
+						{
+							Type[] interfaceTypes = enumeratorType.GetInterfaces();
+							foreach (Type interfaceType in interfaceTypes)
+							{
+								if (interfaceType.IsGenericType
+									&& interfaceType.GetGenericTypeDefinition() == Globals.TypeOfIEnumeratorGeneric
+									&& interfaceType.GetGenericArguments()[0] == collectionContract.ItemType)
+								{
+									ienumeratorInterface = interfaceType;
+									break;
+								}
+							}
+						}
+						if (moveNextMethod == null)
+							moveNextMethod = CollectionDataContract.GetTargetMethodWithName(Globals.MoveNextMethodName, enumeratorType, ienumeratorInterface);
+						if (getCurrentMethod == null)
+							getCurrentMethod = CollectionDataContract.GetTargetMethodWithName(Globals.GetCurrentMethodName, enumeratorType, ienumeratorInterface);
+					}
+				}
+				Type elementType = getCurrentMethod.ReturnType;
+				object currentValue = null; // of elementType
+
+				var enumerator = (IEnumerator) collectionContract.GetEnumeratorMethod.Invoke (objLocal, new object [0]);
+				if (isDictionary)
+				{
+					ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor (Globals.ScanAllMembers, null, new Type[] { Globals.TypeOfIDictionaryEnumerator }, null);
+					enumerator = (IEnumerator) dictEnumCtor.Invoke (new object [] {enumerator});
+				}
+				else if (isGenericDictionary)
+				{
+					Type ctorParam = Globals.TypeOfIEnumeratorGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(keyValueTypes));
+					ConstructorInfo dictEnumCtor = enumeratorType.GetConstructor(Globals.ScanAllMembers, null, new Type[] { ctorParam }, null);
+					enumerator = (IEnumerator) Activator.CreateInstance (enumeratorType, new object [] {enumerator});
+				}
+
+				bool canWriteSimpleDictionary = isDictionary || isGenericDictionary;
+				
+				bool writeSimpleDictionary = canWriteSimpleDictionary | context.UseSimpleDictionaryFormat;
+				PropertyInfo genericDictionaryKeyProperty = null, genericDictionaryValueProperty = null;
+				
+				if (canWriteSimpleDictionary)
+				{
+					Type genericDictionaryKeyValueType = Globals.TypeOfKeyValue.MakeGenericType (keyValueTypes);
+					genericDictionaryKeyProperty = genericDictionaryKeyValueType.GetProperty (JsonGlobals.KeyString);
+					genericDictionaryValueProperty = genericDictionaryKeyValueType.GetProperty (JsonGlobals.ValueString);
+				}
+
+				if (writeSimpleDictionary) {
+					WriteObjectAttribute ();
+					object key, value;
+					var empty_args = new object [0];
+					while ((bool) moveNextMethod.Invoke (enumerator, empty_args)) {
+						currentValue = getCurrentMethod.Invoke (enumerator, empty_args);
+						key = CodeInterpreter.GetMember (genericDictionaryKeyProperty, currentValue);
+						value = CodeInterpreter.GetMember (genericDictionaryValueProperty, currentValue);
+
+						WriteStartElement (key, 0 /*nameIndex*/);
+						WriteValue (genericDictionaryValueProperty.PropertyType, value);
+						WriteEndElement ();
+					}
+				} else {
+					WriteArrayAttribute ();
+
+					var emptyArray = new object [0];
+					while (enumerator != null && enumerator.MoveNext ()) {
+						currentValue = getCurrentMethod.Invoke (enumerator, emptyArray);
+
+						if (incrementCollectionCountMethod == null)
+							XmlFormatGeneratorStatics.IncrementItemCountMethod.Invoke (context, new object [] {1});
+
+						if (!TryWritePrimitive (elementType, () => currentValue, null, null, itemName, 0))
+						{
+							WriteStartElement (itemName, 0);
+							if (isGenericDictionary || isDictionary) {
+								var jc = JsonDataContract.GetJsonDataContract (XmlObjectSerializerWriteContextComplexJson.GetRevisedItemContract (
+								collectionDataContract.ItemContract));
+								// FIXME: this TypeHandle might be wrong; there is no easy way to get Type for currentValue though.
+								DataContractJsonSerializer.WriteJsonValue (jc, writer, currentValue, context, currentValue.GetType ().TypeHandle);
+							}
+							else
+								WriteValue (elementType, currentValue);
+							WriteEndElement ();
+						}
+					}
+				}
+			}
+		}
+
+		int WriteMembers (ClassDataContract classContract, ExtensionDataObject extensionData, ClassDataContract derivedMostClassContract)
+		{
+			int memberCount = (classContract.BaseContract == null) ? 0 : WriteMembers (classContract.BaseContract, extensionData, derivedMostClassContract);
+
+			context.IncrementItemCount (classContract.Members.Count);
+
+			for (int i = 0; i < classContract.Members.Count; i++, memberCount++) {
+
+				DataMember member = classContract.Members[i];
+				Type memberType = member.MemberType;
+				object memberValue = null;
+				if (member.IsGetOnlyCollection)
+					context.StoreIsGetOnlyCollection ();
+				bool doWrite = true, hasMemberValue = false;
+				if (!member.EmitDefaultValue)
+				{
+					hasMemberValue = true;
+					memberValue = LoadMemberValue (member);
+					doWrite = !IsDefaultValue (memberType, memberValue);
+				}
+
+				if (doWrite) {
+
+					bool requiresNameAttribute = DataContractJsonSerializer.CheckIfXmlNameRequiresMapping (classContract.MemberNames [i]);
+					
+					if (requiresNameAttribute || !TryWritePrimitive(memberType, hasMemberValue ? () => memberValue : (Func<object>) null, member.MemberInfo, null /*arrayItemIndex*/, null /*nameLocal*/, i + childElementIndex)) {
+
+						// Note: DataContractSerializer has member-conflict logic here to deal with the schema export
+						//       requirement that the same member can't be of two different types.
+						if (requiresNameAttribute)
+							XmlObjectSerializerWriteContextComplexJson.WriteJsonNameWithMapping (writer, memberNames, i + childElementIndex);
+						else
+							WriteStartElement (null /*nameLocal*/, i + childElementIndex);
+
+						if (memberValue == null)
+							memberValue = LoadMemberValue (member);
+						WriteValue (memberType, memberValue);
+						WriteEndElement ();
+					}
+
+					if (classContract.HasExtensionData)
+						context.WriteExtensionData (writer, extensionData, memberCount);
+				} else if (!member.EmitDefaultValue) {
+					if (member.IsRequired)
+						XmlObjectSerializerWriteContext.ThrowRequiredMemberMustBeEmitted (member.Name, classContract.UnderlyingType);
+				}
+			}
+
+			typeIndex++;
+			childElementIndex += classContract.Members.Count;
+			return memberCount;
+		}
+
+
+		internal bool IsDefaultValue (Type type, object value)
+		{
+			return GetDefaultValue (type).Equals (value);
+		}
+
+		internal object GetDefaultValue(Type type)
+		{
+			if (type.IsValueType)
+			{
+				switch (Type.GetTypeCode(type))
+				{
+				case TypeCode.Boolean:
+					return false;
+				case TypeCode.Char:
+				case TypeCode.SByte:
+				case TypeCode.Byte:
+				case TypeCode.Int16:
+				case TypeCode.UInt16:
+				case TypeCode.Int32:
+				case TypeCode.UInt32:
+					return 0;
+				case TypeCode.Int64:
+				case TypeCode.UInt64:
+					return 0L;
+				case TypeCode.Single:
+					return 0.0F;
+				case TypeCode.Double:
+					return 0.0;
+				case TypeCode.Decimal:
+					return default (decimal);
+				case TypeCode.DateTime:
+					return default (DateTime);
+				}
+			}
+			return null;
+		}
+
+		void WriteStartElement (object nameLocal, int nameIndex)
+		{
+			var name = nameLocal ?? memberNames [nameIndex];
+			XmlDictionaryString namespaceLocal = null;
+			if (nameLocal != null && nameLocal is string)
+				writer.WriteStartElement ((string) name, null);
+			else
+				writer.WriteStartElement ((XmlDictionaryString) name, null);
+		}
+
+		void WriteEndElement ()
+		{
+			writer.WriteEndElement ();
+		}
+
+		void WriteArrayAttribute ()
+		{
+			writer.WriteAttributeString (
+				null /* prefix */,
+				JsonGlobals.typeString /* local name */,
+				string.Empty /* namespace */,
+				JsonGlobals.arrayString /* value */);
+		}
+
+		void WriteObjectAttribute ()
+		{
+			writer.WriteAttributeString (
+				null /* prefix */,
+				JsonGlobals.typeString /* local name */,
+				null /* namespace */,
+				JsonGlobals.objectString /* value */);
+		}
+
+		void WriteValue (Type memberType, object memberValue)
+		{
+			Pointer memberValueRefPointer = null;
+			if (memberType.IsPointer)
+				memberValueRefPointer = (Pointer) JsonFormatGeneratorStatics.BoxPointer.Invoke (null, new object [] {memberValue, memberType});
+			bool isNullableOfT = (memberType.IsGenericType &&
+				memberType.GetGenericTypeDefinition() == Globals.TypeOfNullable);
+			if (memberType.IsValueType && !isNullableOfT)
+			{
+				PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
+				if (primitiveContract != null)
+					primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
+				else
+					InternalSerialize (XmlFormatGeneratorStatics.InternalSerializeMethod, () => memberValue, memberType, false);
+			}
+			else
+			{
+				bool isNull;
+				if (isNullableOfT)
+					memberValue = UnwrapNullableObject(() => memberValue, ref memberType, out isNull); //Leaves !HasValue on stack
+				else
+					isNull = memberValue == null;
+				if (isNull)
+					XmlFormatGeneratorStatics.WriteNullMethod.Invoke (context, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
+				else {
+					PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(memberType);
+					if (primitiveContract != null && primitiveContract.UnderlyingType != Globals.TypeOfObject) {
+						if (isNullableOfT)
+							primitiveContract.XmlFormatContentWriterMethod.Invoke (writer, new object [] {memberValue});
+						else							
+							primitiveContract.XmlFormatContentWriterMethod.Invoke (context, new object [] {writer, memberValue});
+					} else {
+						bool isNull2 = false;
+						if (memberType == Globals.TypeOfObject || //boxed Nullable<T>
+							memberType == Globals.TypeOfValueType ||
+							((IList)Globals.TypeOfNullable.GetInterfaces()).Contains(memberType)) {
+							var unwrappedMemberValue = CodeInterpreter.ConvertValue (memberValue, memberType.GetType (), Globals.TypeOfObject);
+							memberValue = unwrappedMemberValue;
+							isNull2 = memberValue == null;
+						}
+						if (isNull2) {
+							XmlFormatGeneratorStatics.WriteNullMethod.Invoke (context, new object [] {writer, memberType, DataContract.IsTypeSerializable(memberType)});
+						} else {
+							InternalSerialize((isNullableOfT ? XmlFormatGeneratorStatics.InternalSerializeMethod : XmlFormatGeneratorStatics.InternalSerializeReferenceMethod),
+								() => memberValue, memberType, false);
+						}
+					}
+				}
+			}
+		}
+
+		void InternalSerialize (MethodInfo methodInfo, Func<object> memberValue, Type memberType, bool writeXsiType)
+		{
+			var v = memberValue ();
+			var typeHandleValue = Type.GetTypeHandle (v);
+			var isDeclaredType = typeHandleValue.Equals (CodeInterpreter.ConvertValue (v, memberType, Globals.TypeOfObject));
+			methodInfo.Invoke (context, new object [] {writer, memberValue != null ? v : null, isDeclaredType, writeXsiType, DataContract.GetId (memberType.TypeHandle), memberType.TypeHandle});
+		}
+
+		object UnwrapNullableObject(Func<object> memberValue, ref Type memberType, out bool isNull)// Leaves !HasValue on stack
+		{
+			object v = memberValue ();
+			isNull = false;
+			while (memberType.IsGenericType && memberType.GetGenericTypeDefinition () == Globals.TypeOfNullable) {
+				Type innerType = memberType.GetGenericArguments () [0];
+				if ((bool) XmlFormatGeneratorStatics.GetHasValueMethod.MakeGenericMethod (innerType).Invoke (null, new object [] {v}))
+					v = XmlFormatGeneratorStatics.GetNullableValueMethod.MakeGenericMethod (innerType).Invoke (null, new object [] {v});
+				else {
+					isNull = true;
+					v = XmlFormatGeneratorStatics.GetDefaultValueMethod.MakeGenericMethod (memberType).Invoke (null, new object [0]);
+				}
+				memberType = innerType;
+			}
+			
+			return v;
+		}
+
+		bool TryWritePrimitive(Type type, Func<object> value, MemberInfo memberInfo, int? arrayItemIndex, XmlDictionaryString name, int nameIndex)
+		{
+			PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
+			if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
+				return false;
+
+			object callee = null;
+			var args = new List<object> ();
+
+			// load writer
+			if (type.IsValueType)
+				callee = writer;
+			else {
+				callee = context;
+				args.Add (writer);
+			}
+			// load primitive value 
+			if (value != null)
+				args.Add (value ());
+			else if (memberInfo != null)
+				args.Add (CodeInterpreter.GetMember (memberInfo, objLocal));
+			else
+				args.Add (((Array) objLocal).GetValue (new int [] {(int) arrayItemIndex}));
+			// load name
+			if (name != null)
+				args.Add (name);
+			else
+				args.Add (memberNames [nameIndex]);
+			// load namespace
+			args.Add (null);
+			// call method to write primitive
+			primitiveContract.XmlFormatWriterMethod.Invoke (callee, args.ToArray ());
+			return true;
+		}
+
+		bool TryWritePrimitiveArray (Type type, Type itemType, Func<object> value, XmlDictionaryString itemName)
+		{
+			PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
+			if (primitiveContract == null)
+				return false;
+
+			string writeArrayMethod = null;
+			switch (Type.GetTypeCode(itemType))
+			{
+			case TypeCode.Boolean:
+				writeArrayMethod = "WriteJsonBooleanArray";
+				break;
+			case TypeCode.DateTime:
+				writeArrayMethod = "WriteJsonDateTimeArray";
+				break;
+			case TypeCode.Decimal:
+				writeArrayMethod = "WriteJsonDecimalArray";
+				break;
+			case TypeCode.Int32:
+				writeArrayMethod = "WriteJsonInt32Array";
+				break;
+			case TypeCode.Int64:
+				writeArrayMethod = "WriteJsonInt64Array";
+				break;
+			case TypeCode.Single:
+				writeArrayMethod = "WriteJsonSingleArray";
+				break;
+			case TypeCode.Double:
+				writeArrayMethod = "WriteJsonDoubleArray";
+				break;
+			default:
+				break;
+			}
+			if (writeArrayMethod != null)
+			{
+				WriteArrayAttribute ();
+				typeof(JsonWriterDelegator).GetMethod(writeArrayMethod, Globals.ScanAllMembers, null, new Type[] { type, typeof(XmlDictionaryString), typeof(XmlDictionaryString) }, null).Invoke (writer, new object [] {value (), itemName, null});
+				return true;
+			}
+			return false;
+		}
+
+		object LoadMemberValue (DataMember member)
+		{
+			return CodeInterpreter.GetMember (member.MemberInfo, objLocal);
+		}
+	}
 }
 

+ 1 - 0
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources

@@ -9,6 +9,7 @@ ReferenceSources/SR.cs
 ReferenceSources/SR_missing.cs
 ReferenceSources/XmlExceptionHelper.cs
 
+ReferenceSources/BitFlagsGenerator.cs
 ReferenceSources/CodeInterpreter.cs
 ReferenceSources/JsonFormatReaderGenerator_static.cs
 ReferenceSources/JsonFormatWriterGenerator_static.cs

+ 34 - 0
mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/JsonWriterTest.cs

@@ -28,6 +28,7 @@
 using System;
 using System.IO;
 using System.Text;
+using System.Runtime.Serialization;
 using System.Runtime.Serialization.Json;
 using System.Xml;
 using NUnit.Framework;
@@ -51,6 +52,39 @@ namespace MonoTests.System.Runtime.Serialization.Json
 			w = JsonReaderWriterFactory.CreateJsonWriter (ms);
 		}
 
+		/*
+		[Test]
+		public void Dummy_BitFlagsGenerator ()
+		{
+			var b = new BitFlagsGenerator (2);
+			Assert.IsFalse (b.Load (0), "#a1");
+			b.Store (0, false);
+			Assert.IsFalse (b.Load (0), "#a2");
+			b.Store (0, true);
+			Assert.IsTrue (b.Load (0), "#a3");
+			Assert.IsFalse (b.Load (1), "#a4");
+			b.Store (0, false);
+			Assert.IsFalse (b.Load (0), "#a5");
+			Assert.IsFalse (b.Load (1), "#a6");
+
+			Assert.IsFalse (b.Load (1), "#b1");
+			b.Store (1, false);
+			Assert.IsFalse (b.Load (1), "#b2");
+			b.Store (1, true);
+			Assert.IsTrue (b.Load (1), "#b3");
+			b.Store (1, false);
+			Assert.IsFalse (b.Load (1), "#b4");
+
+			var bytes = new byte [2];
+			Assert.IsFalse (BitFlagsGenerator.IsBitSet (bytes, 0), "#c1");
+			BitFlagsGenerator.SetBit (bytes, 0);
+			Assert.IsTrue (BitFlagsGenerator.IsBitSet (bytes, 0), "#c2");
+			Assert.IsFalse (BitFlagsGenerator.IsBitSet (bytes, 1), "#c3");
+			BitFlagsGenerator.SetBit (bytes, 0);
+			Assert.IsTrue (BitFlagsGenerator.IsBitSet (bytes, 0), "#c4");
+		}
+		*/
+
 		[Test]
 		[ExpectedException (typeof (ArgumentNullException))]
 		public void ConstructorNullStream ()