2 using System.Collections.Generic;
5 using System.Reflection;
6 using System.ServiceModel;
7 using System.Collections;
9 namespace System.Runtime.Serialization.Json
12 public class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
14 internal class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
17 EmitTypeInformation emitXsiType;
18 bool perCallXsiTypeAlreadyEmitted;
19 bool useSimpleDictionaryFormat;
21 public XmlObjectSerializerWriteContextComplexJson(DataContractJsonSerializer serializer, DataContract rootTypeDataContract)
23 serializer.MaxItemsInObjectGraph,
24 new StreamingContext(StreamingContextStates.All),
25 serializer.IgnoreExtensionDataObject)
27 this.emitXsiType = serializer.EmitTypeInformation;
28 this.rootTypeDataContract = rootTypeDataContract;
29 this.serializerKnownTypeList = serializer.knownTypeList;
30 this.dataContractSurrogate = serializer.DataContractSurrogate;
31 this.serializeReadOnlyTypes = serializer.SerializeReadOnlyTypes;
32 this.useSimpleDictionaryFormat = serializer.UseSimpleDictionaryFormat;
35 internal static XmlObjectSerializerWriteContextComplexJson CreateContext(DataContractJsonSerializer serializer, DataContract rootTypeDataContract)
37 return new XmlObjectSerializerWriteContextComplexJson(serializer, rootTypeDataContract);
40 internal IList<Type> SerializerKnownTypeList
44 return this.serializerKnownTypeList;
48 public bool UseSimpleDictionaryFormat
52 return this.useSimpleDictionaryFormat;
56 internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, Type dataContractType, string clrTypeName, string clrAssemblyName)
61 internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, DataContract dataContract)
66 internal override void WriteArraySize(XmlWriterDelegator xmlWriter, int size)
70 protected override void WriteTypeInfo(XmlWriterDelegator writer, string dataContractName, string dataContractNamespace)
72 if (this.emitXsiType != EmitTypeInformation.Never)
74 if (string.IsNullOrEmpty(dataContractNamespace))
76 WriteTypeInfo(writer, dataContractName);
80 WriteTypeInfo(writer, string.Concat(dataContractName, JsonGlobals.NameValueSeparatorString, TruncateDefaultDataContractNamespace(dataContractNamespace)));
85 internal static string TruncateDefaultDataContractNamespace(string dataContractNamespace)
87 if (!string.IsNullOrEmpty(dataContractNamespace))
89 if (dataContractNamespace[0] == '#')
91 return string.Concat("\\", dataContractNamespace);
93 else if (dataContractNamespace[0] == '\\')
95 return string.Concat("\\", dataContractNamespace);
97 else if (dataContractNamespace.StartsWith(Globals.DataContractXsdBaseNamespace, StringComparison.Ordinal))
99 return string.Concat("#", dataContractNamespace.Substring(JsonGlobals.DataContractXsdBaseNamespaceLength));
103 return dataContractNamespace;
106 static bool RequiresJsonTypeInfo(DataContract contract)
108 return (contract is ClassDataContract);
111 void WriteTypeInfo(XmlWriterDelegator writer, string typeInformation)
113 writer.WriteAttributeString(null, JsonGlobals.serverTypeString, null, typeInformation);
116 protected override bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
118 if (!((object.ReferenceEquals(contract.Name, declaredContract.Name) &&
119 object.ReferenceEquals(contract.Namespace, declaredContract.Namespace)) ||
120 (contract.Name.Value == declaredContract.Name.Value &&
121 contract.Namespace.Value == declaredContract.Namespace.Value)) &&
122 (contract.UnderlyingType != Globals.TypeOfObjectArray) &&
123 (this.emitXsiType != EmitTypeInformation.Never))
125 // We always deserialize collections assigned to System.Object as object[]
126 // Because of its common and JSON-specific nature,
127 // we don't want to validate known type information for object[]
129 // Don't validate known type information when emitXsiType == Never because
130 // known types are not used without type information in the JSON
132 if (RequiresJsonTypeInfo(contract))
134 perCallXsiTypeAlreadyEmitted = true;
135 WriteTypeInfo(writer, contract.Name.Value, contract.Namespace.Value);
139 // check if the declared type is System.Enum and throw because
140 // __type information cannot be written for enums since it results in invalid JSON.
141 // Without __type, the resulting JSON cannot be deserialized since a number cannot be directly assigned to System.Enum.
142 if (declaredContract.UnderlyingType == typeof(Enum))
144 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException
145 (SR.GetString(SR.EnumTypeNotSupportedByDataContractJsonSerializer, declaredContract.UnderlyingType)));
149 // Return true regardless of whether we actually wrote __type information
150 // E.g. We don't write __type information for enums, but we still want verifyKnownType
151 // to be true for them.
159 public void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
161 internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
164 Type objType = obj.GetType();
165 SerializationInfo serInfo = new SerializationInfo(objType, XmlObjectSerializer.FormatterConverter);
166 GetObjectData(obj, serInfo, GetStreamingContext());
167 if (DataContract.GetClrTypeFullName(objType) != serInfo.FullTypeName)
169 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ChangingFullTypeNameNotSupported, serInfo.FullTypeName, DataContract.GetClrTypeFullName(objType))));
173 base.WriteSerializationInfo(xmlWriter, objType, serInfo);
179 public static DataContract GetRevisedItemContract(DataContract oldItemContract)
181 internal static DataContract GetRevisedItemContract(DataContract oldItemContract)
184 if ((oldItemContract != null) &&
185 oldItemContract.UnderlyingType.IsGenericType &&
186 (oldItemContract.UnderlyingType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue))
188 return DataContract.GetDataContract(oldItemContract.UnderlyingType);
190 return oldItemContract;
193 protected override void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle)
195 JsonDataContract jsonDataContract = JsonDataContract.GetJsonDataContract(dataContract);
196 if (emitXsiType == EmitTypeInformation.Always && !perCallXsiTypeAlreadyEmitted && RequiresJsonTypeInfo(dataContract))
198 WriteTypeInfo(xmlWriter, jsonDataContract.TypeName);
200 perCallXsiTypeAlreadyEmitted = false;
201 DataContractJsonSerializer.WriteJsonValue(jsonDataContract, xmlWriter, obj, this, declaredTypeHandle);
204 protected override void WriteNull(XmlWriterDelegator xmlWriter)
206 DataContractJsonSerializer.WriteJsonNull(xmlWriter);
210 public XmlDictionaryString CollectionItemName
212 internal XmlDictionaryString CollectionItemName
215 get { return JsonGlobals.itemDictionaryString; }
219 public static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlDictionaryString[] memberNames, int index)
221 internal static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlDictionaryString[] memberNames, int index)
224 xmlWriter.WriteStartElement("a", JsonGlobals.itemString, JsonGlobals.itemString);
225 xmlWriter.WriteAttributeString(null, JsonGlobals.itemString, null, memberNames[index].Value);
228 internal override void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode)
230 Type dataType = dataNode.DataType;
231 if (dataType == Globals.TypeOfClassDataNode ||
232 dataType == Globals.TypeOfISerializableDataNode)
234 xmlWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.objectString);
235 base.WriteExtensionDataTypeInfo(xmlWriter, dataNode);
237 else if (dataType == Globals.TypeOfCollectionDataNode)
239 xmlWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.arrayString);
240 // Don't write __type for collections
242 else if (dataType == Globals.TypeOfXmlDataNode)
244 // Don't write type or __type for XML types because we serialize them to strings
246 else if ((dataType == Globals.TypeOfObject) && (dataNode.Value != null))
248 DataContract dc = GetDataContract(dataNode.Value.GetType());
249 if (RequiresJsonTypeInfo(dc))
251 base.WriteExtensionDataTypeInfo(xmlWriter, dataNode);
256 protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
258 DataContract dataContract;
259 bool verifyKnownType = false;
260 bool isDeclaredTypeInterface = declaredType.IsInterface;
262 if (isDeclaredTypeInterface && CollectionDataContract.IsCollectionInterface(declaredType))
264 dataContract = GetDataContract(declaredTypeHandle, declaredType);
266 else if (declaredType.IsArray) // If declared type is array do not write __serverType. Instead write__serverType for each item
268 dataContract = GetDataContract(declaredTypeHandle, declaredType);
272 dataContract = GetDataContract(objectTypeHandle, objectType);
273 DataContract declaredTypeContract = (declaredTypeID >= 0)
274 ? GetDataContract(declaredTypeID, declaredTypeHandle)
275 : GetDataContract(declaredTypeHandle, declaredType);
276 verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, declaredTypeContract);
277 HandleCollectionAssignedToObject(declaredType, ref dataContract, ref obj, ref verifyKnownType);
280 if (isDeclaredTypeInterface)
282 VerifyObjectCompatibilityWithInterface(dataContract, obj, declaredType);
284 SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredType.TypeHandle, declaredType);
287 static void VerifyObjectCompatibilityWithInterface(DataContract contract, object graph, Type declaredType)
289 Type contractType = contract.GetType();
290 if ((contractType == typeof(XmlDataContract)) && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(declaredType))
292 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.XmlObjectAssignedToIncompatibleInterface, graph.GetType(), declaredType)));
295 if ((contractType == typeof(CollectionDataContract)) && !CollectionDataContract.IsCollectionInterface(declaredType))
297 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.CollectionAssignedToIncompatibleInterface, graph.GetType(), declaredType)));
301 void HandleCollectionAssignedToObject(Type declaredType, ref DataContract dataContract, ref object obj, ref bool verifyKnownType)
303 if ((declaredType != dataContract.UnderlyingType) && (dataContract is CollectionDataContract))
307 VerifyType(dataContract, declaredType);
308 verifyKnownType = false;
310 if (((CollectionDataContract)dataContract).Kind == CollectionKind.Dictionary)
312 // Convert non-generic dictionary to generic dictionary
313 IDictionary dictionaryObj = obj as IDictionary;
314 Dictionary<object, object> genericDictionaryObj = new Dictionary<object, object>();
315 foreach (DictionaryEntry entry in dictionaryObj)
317 genericDictionaryObj.Add(entry.Key, entry.Value);
319 obj = genericDictionaryObj;
321 dataContract = GetDataContract(Globals.TypeOfIEnumerable);
325 internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType)
327 bool verifyKnownType = false;
328 Type declaredType = rootTypeDataContract.UnderlyingType;
329 bool isDeclaredTypeInterface = declaredType.IsInterface;
331 if (!(isDeclaredTypeInterface && CollectionDataContract.IsCollectionInterface(declaredType))
332 && !declaredType.IsArray)//Array covariance is not supported in XSD. If declared type is array do not write xsi:type. Instead write xsi:type for each item
334 verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, rootTypeDataContract);
335 HandleCollectionAssignedToObject(declaredType, ref dataContract, ref obj, ref verifyKnownType);
338 if (isDeclaredTypeInterface)
340 VerifyObjectCompatibilityWithInterface(dataContract, obj, declaredType);
342 SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredType.TypeHandle, declaredType);
345 void VerifyType(DataContract dataContract, Type declaredType)
347 bool knownTypesAddedInCurrentScope = false;
348 if (dataContract.KnownDataContracts != null)
350 scopedKnownTypes.Push(dataContract.KnownDataContracts);
351 knownTypesAddedInCurrentScope = true;
354 if (!IsKnownType(dataContract, declaredType))
356 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace)));
359 if (knownTypesAddedInCurrentScope)
361 scopedKnownTypes.Pop();
365 internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type)
367 DataContract dataContract = base.GetDataContract(typeHandle, type);
368 DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
372 internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type type)
374 DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type);
375 DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
379 internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle)
381 DataContract dataContract = base.GetDataContract(id, typeHandle);
382 DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
386 internal static DataContract ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract)
388 if (rootTypeDataContract.StableName == typeQName)
389 return rootTypeDataContract;
391 CollectionDataContract collectionContract = rootTypeDataContract as CollectionDataContract;
392 while (collectionContract != null)
394 DataContract itemContract;
395 if (collectionContract.ItemType.IsGenericType
396 && collectionContract.ItemType.GetGenericTypeDefinition() == typeof(KeyValue<,>))
398 itemContract = context.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
402 itemContract = context.GetDataContract(context.GetSurrogatedType(collectionContract.ItemType));
404 if (itemContract.StableName == typeQName)
408 collectionContract = itemContract as CollectionDataContract;
413 protected override DataContract ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName)
415 return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract);