Moving BSTR conv to native code in SecureStringToBSTR.
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / Json / XmlObjectSerializerWriteContextComplexJson.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Xml;
5 using System.Reflection;
6 using System.ServiceModel;
7 using System.Collections;
8
9 namespace System.Runtime.Serialization.Json
10 {
11 #if USE_REFEMIT
12     public class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
13 #else
14     internal class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
15 #endif
16     {
17         EmitTypeInformation emitXsiType;
18         bool perCallXsiTypeAlreadyEmitted;
19         bool useSimpleDictionaryFormat;
20
21         public XmlObjectSerializerWriteContextComplexJson(DataContractJsonSerializer serializer, DataContract rootTypeDataContract)
22             : base(serializer,
23             serializer.MaxItemsInObjectGraph,
24             new StreamingContext(StreamingContextStates.All),
25             serializer.IgnoreExtensionDataObject)
26         {
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;
33         }
34
35         internal static XmlObjectSerializerWriteContextComplexJson CreateContext(DataContractJsonSerializer serializer, DataContract rootTypeDataContract)
36         {
37             return new XmlObjectSerializerWriteContextComplexJson(serializer, rootTypeDataContract);
38         }
39
40         internal IList<Type> SerializerKnownTypeList
41         {
42             get
43             {
44                 return this.serializerKnownTypeList;
45             }
46         }
47
48         public bool UseSimpleDictionaryFormat
49         {
50             get
51             {
52                 return this.useSimpleDictionaryFormat;
53             }
54         }
55
56         internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, Type dataContractType, string clrTypeName, string clrAssemblyName)
57         {
58             return false;
59         }
60
61         internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, DataContract dataContract)
62         {
63             return false;
64         }
65
66         internal override void WriteArraySize(XmlWriterDelegator xmlWriter, int size)
67         {
68         }
69
70         protected override void WriteTypeInfo(XmlWriterDelegator writer, string dataContractName, string dataContractNamespace)
71         {
72             if (this.emitXsiType != EmitTypeInformation.Never)
73             {
74                 if (string.IsNullOrEmpty(dataContractNamespace))
75                 {
76                     WriteTypeInfo(writer, dataContractName);
77                 }
78                 else
79                 {
80                     WriteTypeInfo(writer, string.Concat(dataContractName, JsonGlobals.NameValueSeparatorString, TruncateDefaultDataContractNamespace(dataContractNamespace)));
81                 }
82             }
83         }
84
85         internal static string TruncateDefaultDataContractNamespace(string dataContractNamespace)
86         {
87             if (!string.IsNullOrEmpty(dataContractNamespace))
88             {
89                 if (dataContractNamespace[0] == '#')
90                 {
91                     return string.Concat("\\", dataContractNamespace);
92                 }
93                 else if (dataContractNamespace[0] == '\\')
94                 {
95                     return string.Concat("\\", dataContractNamespace);
96                 }
97                 else if (dataContractNamespace.StartsWith(Globals.DataContractXsdBaseNamespace, StringComparison.Ordinal))
98                 {
99                     return string.Concat("#", dataContractNamespace.Substring(JsonGlobals.DataContractXsdBaseNamespaceLength));
100                 }
101             }
102
103             return dataContractNamespace;
104         }
105
106         static bool RequiresJsonTypeInfo(DataContract contract)
107         {
108             return (contract is ClassDataContract);
109         }
110
111         void WriteTypeInfo(XmlWriterDelegator writer, string typeInformation)
112         {
113             writer.WriteAttributeString(null, JsonGlobals.serverTypeString, null, typeInformation);
114         }
115
116         protected override bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
117         {
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))
124             {
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[]
128
129                 // Don't validate known type information when emitXsiType == Never because
130                 // known types are not used without type information in the JSON
131
132                 if (RequiresJsonTypeInfo(contract))
133                 {
134                     perCallXsiTypeAlreadyEmitted = true;
135                     WriteTypeInfo(writer, contract.Name.Value, contract.Namespace.Value);
136                 }
137                 else
138                 {
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))
143                     {
144                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException
145                             (SR.GetString(SR.EnumTypeNotSupportedByDataContractJsonSerializer, declaredContract.UnderlyingType)));
146                     }
147
148                 }
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.
152                 return true;
153             }
154             return false;
155         }
156
157
158 #if USE_REFEMIT
159         public void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
160 #else
161         internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
162 #endif
163         {
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)
168             {
169                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ChangingFullTypeNameNotSupported, serInfo.FullTypeName, DataContract.GetClrTypeFullName(objType))));
170             }
171             else
172             {
173                 base.WriteSerializationInfo(xmlWriter, objType, serInfo);
174             }
175         }
176
177
178 #if USE_REFEMIT
179         public static DataContract GetRevisedItemContract(DataContract oldItemContract)
180 #else
181         internal static DataContract GetRevisedItemContract(DataContract oldItemContract)
182 #endif
183         {
184             if ((oldItemContract != null) &&
185                 oldItemContract.UnderlyingType.IsGenericType &&
186                 (oldItemContract.UnderlyingType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue))
187             {
188                 return DataContract.GetDataContract(oldItemContract.UnderlyingType);
189             }
190             return oldItemContract;
191         }
192
193         protected override void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle)
194         {
195             JsonDataContract jsonDataContract = JsonDataContract.GetJsonDataContract(dataContract);
196             if (emitXsiType == EmitTypeInformation.Always && !perCallXsiTypeAlreadyEmitted && RequiresJsonTypeInfo(dataContract))
197             {
198                 WriteTypeInfo(xmlWriter, jsonDataContract.TypeName);
199             }
200             perCallXsiTypeAlreadyEmitted = false;
201             DataContractJsonSerializer.WriteJsonValue(jsonDataContract, xmlWriter, obj, this, declaredTypeHandle);
202         }
203
204         protected override void WriteNull(XmlWriterDelegator xmlWriter)
205         {
206             DataContractJsonSerializer.WriteJsonNull(xmlWriter);
207         }
208
209 #if USE_REFEMIT
210         public XmlDictionaryString CollectionItemName
211 #else
212         internal XmlDictionaryString CollectionItemName
213 #endif
214         {
215             get { return JsonGlobals.itemDictionaryString; }
216         }
217
218 #if USE_REFEMIT
219         public static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlDictionaryString[] memberNames, int index)
220 #else
221         internal static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlDictionaryString[] memberNames, int index)
222 #endif
223         {
224             xmlWriter.WriteStartElement("a", JsonGlobals.itemString, JsonGlobals.itemString);
225             xmlWriter.WriteAttributeString(null, JsonGlobals.itemString, null, memberNames[index].Value);
226         }
227
228         internal override void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode)
229         {
230             Type dataType = dataNode.DataType;
231             if (dataType == Globals.TypeOfClassDataNode ||
232                 dataType == Globals.TypeOfISerializableDataNode)
233             {
234                 xmlWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.objectString);
235                 base.WriteExtensionDataTypeInfo(xmlWriter, dataNode);
236             }
237             else if (dataType == Globals.TypeOfCollectionDataNode)
238             {
239                 xmlWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.arrayString);
240                 // Don't write __type for collections
241             }
242             else if (dataType == Globals.TypeOfXmlDataNode)
243             {
244                 // Don't write type or __type for XML types because we serialize them to strings
245             }
246             else if ((dataType == Globals.TypeOfObject) && (dataNode.Value != null))
247             {
248                 DataContract dc = GetDataContract(dataNode.Value.GetType());
249                 if (RequiresJsonTypeInfo(dc))
250                 {
251                     base.WriteExtensionDataTypeInfo(xmlWriter, dataNode);
252                 }
253             }
254         }
255
256         protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
257         {
258             DataContract dataContract;
259             bool verifyKnownType = false;
260             bool isDeclaredTypeInterface = declaredType.IsInterface;
261
262             if (isDeclaredTypeInterface && CollectionDataContract.IsCollectionInterface(declaredType))
263             {
264                 dataContract = GetDataContract(declaredTypeHandle, declaredType);
265             }
266             else if (declaredType.IsArray) // If declared type is array do not write __serverType. Instead write__serverType for each item
267             {
268                 dataContract = GetDataContract(declaredTypeHandle, declaredType);
269             }
270             else
271             {
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);
278             }
279
280             if (isDeclaredTypeInterface)
281             {
282                 VerifyObjectCompatibilityWithInterface(dataContract, obj, declaredType);
283             }
284             SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredType.TypeHandle, declaredType);
285         }
286
287         static void VerifyObjectCompatibilityWithInterface(DataContract contract, object graph, Type declaredType)
288         {
289             Type contractType = contract.GetType();
290             if ((contractType == typeof(XmlDataContract)) && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(declaredType))
291             {
292                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.XmlObjectAssignedToIncompatibleInterface, graph.GetType(), declaredType)));
293             }
294
295             if ((contractType == typeof(CollectionDataContract)) && !CollectionDataContract.IsCollectionInterface(declaredType))
296             {
297                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.CollectionAssignedToIncompatibleInterface, graph.GetType(), declaredType)));
298             }
299         }
300
301         void HandleCollectionAssignedToObject(Type declaredType, ref DataContract dataContract, ref object obj, ref bool verifyKnownType)
302         {
303             if ((declaredType != dataContract.UnderlyingType) && (dataContract is CollectionDataContract))
304             {
305                 if (verifyKnownType)
306                 {
307                     VerifyType(dataContract, declaredType);
308                     verifyKnownType = false;
309                 }
310                 if (((CollectionDataContract)dataContract).Kind == CollectionKind.Dictionary)
311                 {
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)
316                     {
317                         genericDictionaryObj.Add(entry.Key, entry.Value);
318                     }
319                     obj = genericDictionaryObj;
320                 }
321                 dataContract = GetDataContract(Globals.TypeOfIEnumerable);
322             }
323         }
324
325         internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType)
326         {
327             bool verifyKnownType = false;
328             Type declaredType = rootTypeDataContract.UnderlyingType;
329             bool isDeclaredTypeInterface = declaredType.IsInterface;
330
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
333             {
334                 verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, rootTypeDataContract);
335                 HandleCollectionAssignedToObject(declaredType, ref dataContract, ref obj, ref verifyKnownType);
336             }
337
338             if (isDeclaredTypeInterface)
339             {
340                 VerifyObjectCompatibilityWithInterface(dataContract, obj, declaredType);
341             }
342             SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredType.TypeHandle, declaredType);
343         }
344
345         void VerifyType(DataContract dataContract, Type declaredType)
346         {
347             bool knownTypesAddedInCurrentScope = false;
348             if (dataContract.KnownDataContracts != null)
349             {
350                 scopedKnownTypes.Push(dataContract.KnownDataContracts);
351                 knownTypesAddedInCurrentScope = true;
352             }
353
354             if (!IsKnownType(dataContract, declaredType))
355             {
356                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace)));
357             }
358
359             if (knownTypesAddedInCurrentScope)
360             {
361                 scopedKnownTypes.Pop();
362             }
363         }
364
365         internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type)
366         {
367             DataContract dataContract = base.GetDataContract(typeHandle, type);
368             DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
369             return dataContract;
370         }
371
372         internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type type)
373         {
374             DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type);
375             DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
376             return dataContract;
377         }
378
379         internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle)
380         {
381             DataContract dataContract = base.GetDataContract(id, typeHandle);
382             DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
383             return dataContract;
384         }
385
386         internal static DataContract ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract)
387         {
388             if (rootTypeDataContract.StableName == typeQName)
389                 return rootTypeDataContract;
390
391             CollectionDataContract collectionContract = rootTypeDataContract as CollectionDataContract;
392             while (collectionContract != null)
393             {
394                 DataContract itemContract;
395                 if (collectionContract.ItemType.IsGenericType
396                     && collectionContract.ItemType.GetGenericTypeDefinition() == typeof(KeyValue<,>))
397                 {
398                     itemContract = context.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
399                 }
400                 else
401                 {
402                     itemContract = context.GetDataContract(context.GetSurrogatedType(collectionContract.ItemType));
403                 }
404                 if (itemContract.StableName == typeQName)
405                 {
406                     return itemContract;
407                 }
408                 collectionContract = itemContract as CollectionDataContract;
409             }
410             return null;
411         }
412
413         protected override DataContract ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName)
414         {
415             return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract);
416         }
417     }
418 }