[S.R.Serialization] add Json reader/writer interpreters.
authorAtsushi Eno <atsushieno@gmail.com>
Thu, 16 Apr 2015 06:35:01 +0000 (15:35 +0900)
committerAtsushi Eno <atsushieno@gmail.com>
Fri, 24 Apr 2015 05:36:55 +0000 (14:36 +0900)
They don't pass all the tests in System.ServiceModel.Web yet.

mcs/class/System.Runtime.Serialization/ReferenceSource.common.sources
mcs/class/System.Runtime.Serialization/ReferenceSources/BitFlagsGenerator.cs [new file with mode: 0644]
mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatReaderGenerator_static.cs
mcs/class/System.Runtime.Serialization/ReferenceSources/JsonFormatWriterGenerator_static.cs
mcs/class/System.Runtime.Serialization/System.Runtime.Serialization.dll.sources
mcs/class/System.ServiceModel.Web/Test/System.Runtime.Serialization.Json/JsonWriterTest.cs

index 8adfd32bb1a4657db0ec7bca9546ab7f6043dcfa..b7e92f87afba05745c2c47f4115fe79e30320f0e 100644 (file)
@@ -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
diff --git a/mcs/class/System.Runtime.Serialization/ReferenceSources/BitFlagsGenerator.cs b/mcs/class/System.Runtime.Serialization/ReferenceSources/BitFlagsGenerator.cs
new file mode 100644 (file)
index 0000000..73a2f1e
--- /dev/null
@@ -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));
+               }
+       }
+}
index 35bc69ef471b342bd017a6c73c36f03953e295a2..5079c255a9d54f0eceeda7adeda013deaaa219b8 100644 (file)
@@ -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
+               }
+       }
+}
index 59c6f5280f0bfe219b0bb3461f543b9dd13e11e2..28b4186f9aa3ad0ec05220598916bfea229ceb46 100644 (file)
@@ -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);
+               }
+       }
 }
 
index 4eaa3d9fa99c942c97e5173a200345436dbcb747..8c375632f505f92d178b2c160fd44212e6f58d80 100644 (file)
@@ -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
index 4ea004a34e86c7b60d01da5951b7069d8ff636d5..b6e42b90bb0dafaee233cf63ea37187a3bcd2a46 100644 (file)
@@ -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 ()