Bug 2843 - WCF: SerializationException when processing response that contains a nullable enum
Summary: WCF: SerializationException when processing response that contains a nullable...
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 5.0
Hardware: Macintosh Mac OS
: Normal normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2012-01-11 08:32 UTC by René Ruppert
Modified: 2014-06-15 17:15 UTC (History)
8 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Comment 4 René Ruppert 2012-01-11 09:56:09 UTC
And another bit of info: we tracked the issue down to Nullable enums. It does not matter if it is decorated with Flags. If an enum is nullable, the (de)serializer will fail, no matter if the actual value is a string or an integer.
Comment 7 René Ruppert 2012-01-16 03:32:22 UTC
Here's one mor bug that seems to be related. Maybe it helps resolving the issue: http://bugzilla.xamarin.com/show_bug.cgi?id=2859

Any estimation about a fix for this problem?
Comment 10 Miguel de Icaza [MSFT] 2012-01-17 20:34:39 UTC
Mono does not currently support nullable values in WCF's deserializer.

That said, the following patch is an attempt at adding nullable support for deserialization.   Sadly, I do not believe this is complete. 

It lets the attached sample run oAPIConnector.APIDataroom.GetDatarooms, but it still fails with oAPIConnector.APIDataroom.GetTree


diff --git a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/KnownTypeCollection.cs b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/KnownTypeCollection.cs
index 8713ef3..9f344ce 100755
--- a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/KnownTypeCollection.cs
+++ b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/KnownTypeCollection.cs
@@ -605,8 +605,13 @@ namespace System.Runtime.Serialization
 		static QName GetEnumQName (Type type)
 		{
 			string name = null, ns = null;
+			Type testType = type;
+			
+			if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>)){
+				testType = Nullable.GetUnderlyingType (type);
+			}
 
-			if (!type.IsEnum)
+			if (!testType.IsEnum)
 				return null;
 
 			var dca = GetAttribute<DataContractAttribute> (type);
@@ -622,6 +627,8 @@ namespace System.Runtime.Serialization
 			if (name == null)
 				name = type.Namespace == null ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
 
+			if (testType != type)
+				name = "NullableOf" + name;
 			return new QName (name, ns);
 		}
 
@@ -944,6 +951,7 @@ namespace System.Runtime.Serialization
 
 		private EnumMap RegisterEnum (Type type)
 		{
+			Type otype = type;
 			QName qname = GetEnumQName (type);
 			if (qname == null)
 				return null;
@@ -951,6 +959,10 @@ namespace System.Runtime.Serialization
 			if (FindUserMap (qname) != null)
 				throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
 
+			if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>)){
+				contracts.Add (new EnumMap (Nullable.GetUnderlyingType (type), qname, this));
+			}
+			
 			EnumMap ret =
 				new EnumMap (type, qname, this);
 			contracts.Add (ret);
diff --git a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs
index 3752a0c..eb5fd91 100644
--- a/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs
+++ b/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/XmlFormatterDeserializer.cs
@@ -236,9 +236,20 @@ namespace System.Runtime.Serialization
 
 		Type GetTypeFromNamePair (string name, string ns)
 		{
+			bool makeNullable = false;
+#if false
+			if (name.StartsWith ("NullableOf", StringComparison.Ordinal)){
+				name = name.Substring (10);
+				makeNullable = true;
+			}
+#endif
+			
 			Type p = KnownTypeCollection.GetPrimitiveTypeFromName (new QName (name, ns));
-			if (p != null)
+			if (p != null){
+				if (makeNullable)
+					return typeof (Nullable<>).MakeGenericType (p);
 				return p;
+			}
 			bool makeArray = false;
 			if (name.StartsWith ("ArrayOf", StringComparison.Ordinal)) {
 				name = name.Substring (7); // strip "ArrayOf"
@@ -286,7 +297,7 @@ namespace System.Runtime.Serialization
 					}
 
 					if (clrns != null && t.Name == name && t.Namespace == clrns)
-						return makeArray ? t.MakeArrayType () : t;
+						return makeArray ? t.MakeArrayType () : makeNullable ? typeof (Nullable<>).MakeGenericType (t) : t;
 				}
 			}
 			throw new XmlException (String.Format ("Type not found; name: {0}, namespace: {1}", name, ns));
Comment 11 Atsushi Eno 2012-01-18 01:22:21 UTC
I believe that if we are going to handle nullable types in our serializer then there should be another SerializationMap that cares those types in many aspect, which is not limited to the exact issues discussed here (i.e. type import validity check, serialized xml names, xsd generation and xsd import) and not limited to enums.
And since we don't really know those requirements, it should be examined and written in nunit tests and then implemented. I don't think this can be done anytime soon by people's part time hacking.
It had rather better be documented as a known limitation (besides that WCF is already explicitly documented as alpha quality) and requests for this issue should rather be treated as a feature request.
As a workaround: those nullable enums can be converted into string or something in  the data contract code.
Comment 15 Gonzalo Paniagua Javier 2012-03-14 16:24:16 UTC
Applied the fix from Vinicius in master.
Comment 16 Sebastien Pouliot 2012-03-19 13:13:26 UTC
the fix was also backported in mono-2-10 and mobiel-master (by Gonzalo)

it's now also in 5.2-series for the next MonoTouch stable release dc8c35db103fcda7e87a710350b2ffcfb8f33f55
Comment 17 Shane van Wyk 2013-07-23 21:57:04 UTC
Sebastien Pouliot: In what version of mono-2-10 was this fix back ported to? We still currently have an issue around this. When the Nullable node is in fact null the serializer does not complain. However when the Nullable Enum has a value assigned to it, it then throws the exception. Is there something we are missing. We are currently running the latest possible version of mono on ubuntu which is 2.10.8.1 i think.
Comment 18 Chris Andrews 2013-07-31 20:15:27 UTC
Not able to get this to work either. It will work without serialized data generated by the mono serializer, but not with data generated by the MS serializer. Mono adds a 'type' attribute to the node, MS does not. Add a 'type' attribute or make the enum non-nullable and it works. Unfortunately, in my case, adding a type attribute is not possible, since I'm consuming a large public service. Here is test code:

[System.Runtime.Serialization.DataContractAttribute(Name="A", Namespace = "http://foo.bar")]

	public enum A : int

	{

		[System.Runtime.Serialization.EnumMemberAttribute()]

		TheValue = 0,        

	}



	[System.Runtime.Serialization.DataContractAttribute(Name="B", Namespace = "http://foo.bar")]

	[System.SerializableAttribute()]

	public class B

	{

		[System.Runtime.Serialization.DataMemberAttribute()]

		public System.Nullable<A> item { get; set; }

	}



	public class SerlializationTest

	{

		public void TestMethod1()

		{

			var b = new B {item = A.TheValue};

			var mem = new System.IO.MemoryStream();

			var ser = new System.Runtime.Serialization.DataContractSerializer(typeof(B));

			ser.WriteObject(mem, b);



			String mystr = System.Text.Encoding.UTF8.GetString(mem.ToArray());

			Console.WriteLine ("str = " + mystr);



			// Microsoft serializer produces, mystr="<B xmlns=\"http://foo.bar\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><item>TheValue</item></B>"

			// Mono 2.10.9, mystr = "<B xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://foo.bar"><item xmlns:d2p1="http://schemas.datacontract.org/2004/07/System" i:type="A">TheValue</item></B>"



			// Set mystr to what MS.NET produces fails

			mystr = "<B xmlns=\"http://foo.bar\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><item>TheValue</item></B>";



			// If you add 'i:type="A"' to item, mono deserialization works 

			// OR if you make type B non-nullable, it works.

			// nullable enum without an i:type attribute does not work.

			// mystr = "<B xmlns=\"http://foo.bar\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><item i:type=\"A\">TheValue</item></B>";



			var newStream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(mystr));

			var newB = (B)ser.ReadObject(newStream);



			Console.WriteLine("B.item = " + newB.item);

		}

    }
Comment 19 Shane van Wyk 2013-07-31 21:21:52 UTC
@Chris Andrews 

Does the "Name" part in the DataContractAttribute set the type?
Comment 20 Shane van Wyk 2014-06-15 17:15:55 UTC
This is marked as resolved but I do not believe it is.

I'm running Mono 3.2.8 and it still does not work.

Can anyone comment on this?