|
|
@@ -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
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|