2016-08-03 10:59:49 +00:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.Xml ;
using System.Reflection ;
2016-11-10 13:04:39 +00:00
#if ! MONO
2016-08-03 10:59:49 +00:00
using System.ServiceModel ;
2016-11-10 13:04:39 +00:00
#endif
2016-08-03 10:59:49 +00:00
using System.Collections ;
namespace System.Runtime.Serialization.Json
{
#if USE_REFEMIT
public class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
#else
internal class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
#endif
{
EmitTypeInformation emitXsiType ;
bool perCallXsiTypeAlreadyEmitted ;
bool useSimpleDictionaryFormat ;
public XmlObjectSerializerWriteContextComplexJson ( DataContractJsonSerializer serializer , DataContract rootTypeDataContract )
: base ( serializer ,
serializer . MaxItemsInObjectGraph ,
new StreamingContext ( StreamingContextStates . All ) ,
serializer . IgnoreExtensionDataObject )
{
this . emitXsiType = serializer . EmitTypeInformation ;
this . rootTypeDataContract = rootTypeDataContract ;
this . serializerKnownTypeList = serializer . knownTypeList ;
this . dataContractSurrogate = serializer . DataContractSurrogate ;
this . serializeReadOnlyTypes = serializer . SerializeReadOnlyTypes ;
this . useSimpleDictionaryFormat = serializer . UseSimpleDictionaryFormat ;
}
internal static XmlObjectSerializerWriteContextComplexJson CreateContext ( DataContractJsonSerializer serializer , DataContract rootTypeDataContract )
{
return new XmlObjectSerializerWriteContextComplexJson ( serializer , rootTypeDataContract ) ;
}
internal IList < Type > SerializerKnownTypeList
{
get
{
return this . serializerKnownTypeList ;
}
}
public bool UseSimpleDictionaryFormat
{
get
{
return this . useSimpleDictionaryFormat ;
}
}
internal override bool WriteClrTypeInfo ( XmlWriterDelegator xmlWriter , Type dataContractType , string clrTypeName , string clrAssemblyName )
{
return false ;
}
internal override bool WriteClrTypeInfo ( XmlWriterDelegator xmlWriter , DataContract dataContract )
{
return false ;
}
internal override void WriteArraySize ( XmlWriterDelegator xmlWriter , int size )
{
}
protected override void WriteTypeInfo ( XmlWriterDelegator writer , string dataContractName , string dataContractNamespace )
{
if ( this . emitXsiType ! = EmitTypeInformation . Never )
{
if ( string . IsNullOrEmpty ( dataContractNamespace ) )
{
WriteTypeInfo ( writer , dataContractName ) ;
}
else
{
WriteTypeInfo ( writer , string . Concat ( dataContractName , JsonGlobals . NameValueSeparatorString , TruncateDefaultDataContractNamespace ( dataContractNamespace ) ) ) ;
}
}
}
internal static string TruncateDefaultDataContractNamespace ( string dataContractNamespace )
{
if ( ! string . IsNullOrEmpty ( dataContractNamespace ) )
{
if ( dataContractNamespace [ 0 ] = = '#' )
{
return string . Concat ( "\\" , dataContractNamespace ) ;
}
else if ( dataContractNamespace [ 0 ] = = '\\' )
{
return string . Concat ( "\\" , dataContractNamespace ) ;
}
else if ( dataContractNamespace . StartsWith ( Globals . DataContractXsdBaseNamespace , StringComparison . Ordinal ) )
{
return string . Concat ( "#" , dataContractNamespace . Substring ( JsonGlobals . DataContractXsdBaseNamespaceLength ) ) ;
}
}
return dataContractNamespace ;
}
static bool RequiresJsonTypeInfo ( DataContract contract )
{
return ( contract is ClassDataContract ) ;
}
void WriteTypeInfo ( XmlWriterDelegator writer , string typeInformation )
{
writer . WriteAttributeString ( null , JsonGlobals . serverTypeString , null , typeInformation ) ;
}
protected override bool WriteTypeInfo ( XmlWriterDelegator writer , DataContract contract , DataContract declaredContract )
{
if ( ! ( ( object . ReferenceEquals ( contract . Name , declaredContract . Name ) & &
object . ReferenceEquals ( contract . Namespace , declaredContract . Namespace ) ) | |
( contract . Name . Value = = declaredContract . Name . Value & &
contract . Namespace . Value = = declaredContract . Namespace . Value ) ) & &
( contract . UnderlyingType ! = Globals . TypeOfObjectArray ) & &
( this . emitXsiType ! = EmitTypeInformation . Never ) )
{
// We always deserialize collections assigned to System.Object as object[]
// Because of its common and JSON-specific nature,
// we don't want to validate known type information for object[]
// Don't validate known type information when emitXsiType == Never because
// known types are not used without type information in the JSON
if ( RequiresJsonTypeInfo ( contract ) )
{
perCallXsiTypeAlreadyEmitted = true ;
WriteTypeInfo ( writer , contract . Name . Value , contract . Namespace . Value ) ;
}
else
{
// check if the declared type is System.Enum and throw because
// __type information cannot be written for enums since it results in invalid JSON.
// Without __type, the resulting JSON cannot be deserialized since a number cannot be directly assigned to System.Enum.
if ( declaredContract . UnderlyingType = = typeof ( Enum ) )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new SerializationException
( SR . GetString ( SR . EnumTypeNotSupportedByDataContractJsonSerializer , declaredContract . UnderlyingType ) ) ) ;
}
}
// Return true regardless of whether we actually wrote __type information
// E.g. We don't write __type information for enums, but we still want verifyKnownType
// to be true for them.
return true ;
}
return false ;
}
#if USE_REFEMIT
public void WriteJsonISerializable ( XmlWriterDelegator xmlWriter , ISerializable obj )
#else
internal void WriteJsonISerializable ( XmlWriterDelegator xmlWriter , ISerializable obj )
#endif
{
Type objType = obj . GetType ( ) ;
SerializationInfo serInfo = new SerializationInfo ( objType , XmlObjectSerializer . FormatterConverter ) ;
GetObjectData ( obj , serInfo , GetStreamingContext ( ) ) ;
if ( DataContract . GetClrTypeFullName ( objType ) ! = serInfo . FullTypeName )
{
throw System . Runtime . Serialization . DiagnosticUtility . ExceptionUtility . ThrowHelperError ( XmlObjectSerializer . CreateSerializationException ( SR . GetString ( SR . ChangingFullTypeNameNotSupported , serInfo . FullTypeName , DataContract . GetClrTypeFullName ( objType ) ) ) ) ;
}
else
{
base . WriteSerializationInfo ( xmlWriter , objType , serInfo ) ;
}
}
#if USE_REFEMIT
public static DataContract GetRevisedItemContract ( DataContract oldItemContract )
#else
internal static DataContract GetRevisedItemContract ( DataContract oldItemContract )
#endif
{
if ( ( oldItemContract ! = null ) & &
oldItemContract . UnderlyingType . IsGenericType & &
( oldItemContract . UnderlyingType . GetGenericTypeDefinition ( ) = = Globals . TypeOfKeyValue ) )
{
return DataContract . GetDataContract ( oldItemContract . UnderlyingType ) ;
}
return oldItemContract ;
}
protected override void WriteDataContractValue ( DataContract dataContract , XmlWriterDelegator xmlWriter , object obj , RuntimeTypeHandle declaredTypeHandle )
{
JsonDataContract jsonDataContract = JsonDataContract . GetJsonDataContract ( dataContract ) ;
if ( emitXsiType = = EmitTypeInformation . Always & & ! perCallXsiTypeAlreadyEmitted & & RequiresJsonTypeInfo ( dataContract ) )
{
WriteTypeInfo ( xmlWriter , jsonDataContract . TypeName ) ;
}
perCallXsiTypeAlreadyEmitted = false ;
DataContractJsonSerializer . WriteJsonValue ( jsonDataContract , xmlWriter , obj , this , declaredTypeHandle ) ;
}
protected override void WriteNull ( XmlWriterDelegator xmlWriter )
{
DataContractJsonSerializer . WriteJsonNull ( xmlWriter ) ;
}
#if USE_REFEMIT
public XmlDictionaryString CollectionItemName
#else
internal XmlDictionaryString CollectionItemName
#endif
{
get { return JsonGlobals . itemDictionaryString ; }
}
#if USE_REFEMIT
public static void WriteJsonNameWithMapping ( XmlWriterDelegator xmlWriter , XmlDictionaryString [ ] memberNames , int index )
#else
internal static void WriteJsonNameWithMapping ( XmlWriterDelegator xmlWriter , XmlDictionaryString [ ] memberNames , int index )
#endif
{
xmlWriter . WriteStartElement ( "a" , JsonGlobals . itemString , JsonGlobals . itemString ) ;
xmlWriter . WriteAttributeString ( null , JsonGlobals . itemString , null , memberNames [ index ] . Value ) ;
}
internal override void WriteExtensionDataTypeInfo ( XmlWriterDelegator xmlWriter , IDataNode dataNode )
{
Type dataType = dataNode . DataType ;
if ( dataType = = Globals . TypeOfClassDataNode | |
dataType = = Globals . TypeOfISerializableDataNode )
{
xmlWriter . WriteAttributeString ( null , JsonGlobals . typeString , null , JsonGlobals . objectString ) ;
base . WriteExtensionDataTypeInfo ( xmlWriter , dataNode ) ;
}
else if ( dataType = = Globals . TypeOfCollectionDataNode )
{
xmlWriter . WriteAttributeString ( null , JsonGlobals . typeString , null , JsonGlobals . arrayString ) ;
// Don't write __type for collections
}
else if ( dataType = = Globals . TypeOfXmlDataNode )
{
// Don't write type or __type for XML types because we serialize them to strings
}
else if ( ( dataType = = Globals . TypeOfObject ) & & ( dataNode . Value ! = null ) )
{
DataContract dc = GetDataContract ( dataNode . Value . GetType ( ) ) ;
if ( RequiresJsonTypeInfo ( dc ) )
{
base . WriteExtensionDataTypeInfo ( xmlWriter , dataNode ) ;
}
}
}
protected override void SerializeWithXsiType ( XmlWriterDelegator xmlWriter , object obj , RuntimeTypeHandle objectTypeHandle , Type objectType , int declaredTypeID , RuntimeTypeHandle declaredTypeHandle , Type declaredType )
{
DataContract dataContract ;
bool verifyKnownType = false ;
bool isDeclaredTypeInterface = declaredType . IsInterface ;
if ( isDeclaredTypeInterface & & CollectionDataContract . IsCollectionInterface ( declaredType ) )
{
dataContract = GetDataContract ( declaredTypeHandle , declaredType ) ;
}
else if ( declaredType . IsArray ) // If declared type is array do not write __serverType. Instead write__serverType for each item
{
dataContract = GetDataContract ( declaredTypeHandle , declaredType ) ;
}
else
{
dataContract = GetDataContract ( objectTypeHandle , objectType ) ;
DataContract declaredTypeContract = ( declaredTypeID > = 0 )
? GetDataContract ( declaredTypeID , declaredTypeHandle )
: GetDataContract ( declaredTypeHandle , declaredType ) ;
verifyKnownType = WriteTypeInfo ( xmlWriter , dataContract , declaredTypeContract ) ;
HandleCollectionAssignedToObject ( declaredType , ref dataContract , ref obj , ref verifyKnownType ) ;
}
if ( isDeclaredTypeInterface )
{
VerifyObjectCompatibilityWithInterface ( dataContract , obj , declaredType ) ;
}
SerializeAndVerifyType ( dataContract , xmlWriter , obj , verifyKnownType , declaredType . TypeHandle , declaredType ) ;
}
static void VerifyObjectCompatibilityWithInterface ( DataContract contract , object graph , Type declaredType )
{
Type contractType = contract . GetType ( ) ;
if ( ( contractType = = typeof ( XmlDataContract ) ) & & ! Globals . TypeOfIXmlSerializable . IsAssignableFrom ( declaredType ) )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( XmlObjectSerializer . CreateSerializationException ( SR . GetString ( SR . XmlObjectAssignedToIncompatibleInterface , graph . GetType ( ) , declaredType ) ) ) ;
}
if ( ( contractType = = typeof ( CollectionDataContract ) ) & & ! CollectionDataContract . IsCollectionInterface ( declaredType ) )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( XmlObjectSerializer . CreateSerializationException ( SR . GetString ( SR . CollectionAssignedToIncompatibleInterface , graph . GetType ( ) , declaredType ) ) ) ;
}
}
void HandleCollectionAssignedToObject ( Type declaredType , ref DataContract dataContract , ref object obj , ref bool verifyKnownType )
{
if ( ( declaredType ! = dataContract . UnderlyingType ) & & ( dataContract is CollectionDataContract ) )
{
if ( verifyKnownType )
{
VerifyType ( dataContract , declaredType ) ;
verifyKnownType = false ;
}
if ( ( ( CollectionDataContract ) dataContract ) . Kind = = CollectionKind . Dictionary )
{
// Convert non-generic dictionary to generic dictionary
IDictionary dictionaryObj = obj as IDictionary ;
Dictionary < object , object > genericDictionaryObj = new Dictionary < object , object > ( ) ;
foreach ( DictionaryEntry entry in dictionaryObj )
{
genericDictionaryObj . Add ( entry . Key , entry . Value ) ;
}
obj = genericDictionaryObj ;
}
dataContract = GetDataContract ( Globals . TypeOfIEnumerable ) ;
}
}
internal override void SerializeWithXsiTypeAtTopLevel ( DataContract dataContract , XmlWriterDelegator xmlWriter , object obj , RuntimeTypeHandle originalDeclaredTypeHandle , Type graphType )
{
bool verifyKnownType = false ;
Type declaredType = rootTypeDataContract . UnderlyingType ;
bool isDeclaredTypeInterface = declaredType . IsInterface ;
if ( ! ( isDeclaredTypeInterface & & CollectionDataContract . IsCollectionInterface ( declaredType ) )
& & ! 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
{
verifyKnownType = WriteTypeInfo ( xmlWriter , dataContract , rootTypeDataContract ) ;
HandleCollectionAssignedToObject ( declaredType , ref dataContract , ref obj , ref verifyKnownType ) ;
}
if ( isDeclaredTypeInterface )
{
VerifyObjectCompatibilityWithInterface ( dataContract , obj , declaredType ) ;
}
SerializeAndVerifyType ( dataContract , xmlWriter , obj , verifyKnownType , declaredType . TypeHandle , declaredType ) ;
}
void VerifyType ( DataContract dataContract , Type declaredType )
{
bool knownTypesAddedInCurrentScope = false ;
if ( dataContract . KnownDataContracts ! = null )
{
scopedKnownTypes . Push ( dataContract . KnownDataContracts ) ;
knownTypesAddedInCurrentScope = true ;
}
if ( ! IsKnownType ( dataContract , declaredType ) )
{
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( XmlObjectSerializer . CreateSerializationException ( SR . GetString ( SR . DcTypeNotFoundOnSerialize , DataContract . GetClrTypeFullName ( dataContract . UnderlyingType ) , dataContract . StableName . Name , dataContract . StableName . Namespace ) ) ) ;
}
if ( knownTypesAddedInCurrentScope )
{
scopedKnownTypes . Pop ( ) ;
}
}
internal override DataContract GetDataContract ( RuntimeTypeHandle typeHandle , Type type )
{
DataContract dataContract = base . GetDataContract ( typeHandle , type ) ;
DataContractJsonSerializer . CheckIfTypeIsReference ( dataContract ) ;
return dataContract ;
}
internal override DataContract GetDataContractSkipValidation ( int typeId , RuntimeTypeHandle typeHandle , Type type )
{
DataContract dataContract = base . GetDataContractSkipValidation ( typeId , typeHandle , type ) ;
DataContractJsonSerializer . CheckIfTypeIsReference ( dataContract ) ;
return dataContract ;
}
internal override DataContract GetDataContract ( int id , RuntimeTypeHandle typeHandle )
{
DataContract dataContract = base . GetDataContract ( id , typeHandle ) ;
DataContractJsonSerializer . CheckIfTypeIsReference ( dataContract ) ;
return dataContract ;
}
internal static DataContract ResolveJsonDataContractFromRootDataContract ( XmlObjectSerializerContext context , XmlQualifiedName typeQName , DataContract rootTypeDataContract )
{
if ( rootTypeDataContract . StableName = = typeQName )
return rootTypeDataContract ;
CollectionDataContract collectionContract = rootTypeDataContract as CollectionDataContract ;
while ( collectionContract ! = null )
{
DataContract itemContract ;
if ( collectionContract . ItemType . IsGenericType
& & collectionContract . ItemType . GetGenericTypeDefinition ( ) = = typeof ( KeyValue < , > ) )
{
itemContract = context . GetDataContract ( Globals . TypeOfKeyValuePair . MakeGenericType ( collectionContract . ItemType . GetGenericArguments ( ) ) ) ;
}
else
{
itemContract = context . GetDataContract ( context . GetSurrogatedType ( collectionContract . ItemType ) ) ;
}
if ( itemContract . StableName = = typeQName )
{
return itemContract ;
}
collectionContract = itemContract as CollectionDataContract ;
}
return null ;
}
protected override DataContract ResolveDataContractFromRootDataContract ( XmlQualifiedName typeQName )
{
return XmlObjectSerializerWriteContextComplexJson . ResolveJsonDataContractFromRootDataContract ( this , typeQName , rootTypeDataContract ) ;
}
}
}