Bug 18171 - [System.Xml.Linq, System.Runtime.Serialization] XElement implements IXmlSerializable, but lacks parameterless constructor, and has incorrect QName in KnownTypeCollection
Summary: [System.Xml.Linq, System.Runtime.Serialization] XElement implements IXmlSeria...
Alias: None
Product: Class Libraries
Classification: Mono
Component: System.XML ()
Version: master
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
Depends on:
Reported: 2014-03-04 22:11 UTC by Brendan Zagaeski (Xamarin Team, assistant)
Modified: 2016-05-05 00:07 UTC (History)
2 users (show)

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

Tests (5.16 KB, patch)
2014-03-04 22:11 UTC, Brendan Zagaeski (Xamarin Team, assistant)
Proposed fix (1.92 KB, patch)
2014-03-04 22:24 UTC, Brendan Zagaeski (Xamarin Team, assistant)
Test case (5.70 KB, application/zip)
2016-05-05 00:06 UTC, Brendan Zagaeski (Xamarin Team, assistant)

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 GitHub or Developer Community 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:

Description Brendan Zagaeski (Xamarin Team, assistant) 2014-03-04 22:11:53 UTC
Created attachment 6221 [details]

The "Deserialize_System_Xml_Linq_XElement_AnyTypeArray" test from the attached "Tests" currently fails on Mono but succeeds on .NET. The "Proposed fix" includes three changes that allow the test to pass. Note that this patch does _not_ yet fix the case of serializing a plain `XElement`. It only fixes `XElement` used as a `KnownType`/`AnyType`.

## Changes in the "Deserialization" patch

1. Change GetSerializableQName() to allow non-empty qualified names for types with `XmlSchemaProviderAttribute.IsAny == true`. The original version of this line was added to help with bug #11916. Since that bug is also about `XElement`, I suspect this proposed change is OK.

Before this change, the test fails with the following stack trace (abbreviated):

> System.Reflection.ReflectionTypeLoadException : The classes in the module cannot be loaded.
>   at (wrapper managed-to-native) System.Reflection.Assembly:GetTypes (System.Reflection.Assembly,bool)
>   at System.Reflection.Assembly.GetTypes () [0x00000] in /private/tmp/source/bockbuild-mono-3.2.6/profiles/mono-mac-xamarin/build-root/mono-3.2.6/mcs/class/corlib/System.Reflection/Assembly.cs:351 
>   at System.Runtime.Serialization.XmlFormatterDeserializer.GetTypeFromNamePair (System.String name, System.String ns)

2. Add a private parameterless constructor to `XElement`.

As discussed on bug #4507, `XElement` lacks a parameterless constructor. This conflicts with the requirement stated on [1] that any class that implements `IXmlSerializable` "must have a parameterless constructor." On .NET, although `XElement` does not have a _public_ parameterless constructor, it does have a _private_ parameterless constructor.

> [1] http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

Before this change, the test fails with the following stack trace (abbreviated):

> System.MissingMethodException : Default constructor not found for type System.Xml.Linq.XElement
>   at System.Activator.CreateInstance (System.Type type, Boolean nonPublic) [0x00094] in /private/tmp/source/bockbuild-mono-3.2.6/profiles/mono-mac-xamarin/build-root/mono-3.2.6/mcs/class/corlib/System/Activator.cs:326 
>   at System.Runtime.Serialization.XmlSerializableMap.DeserializeObject (System.Xml.XmlReader reader, System.Runtime.Serialization.XmlFormatterDeserializer deserializer)

3. Switch `XElement.ReadXml()` to use `XElement.LoadCore()` rather than `XNode.ReadFrom()`. I think `XElement.ReadXml()` probably only needs to handle cases where the XML input is an `XmlNodeType.Element`, so this seems like an acceptable change. The `used_parameterless_construtor` check on `reader.Read()` might not be strictly necessary. Removing the check does not break any of the attached tests.

Before this change, the test fails with the following stack trace (abbreviated):
> System.NullReferenceException : Object reference not set to an instance of an object
>   at System.Xml.Linq.XElement.WriteTo (System.Xml.XmlWriter writer)

Additionally, before this change the proposed `ParseVsReadXml` test fails with the following stack trace (abbreviated):

> System.InvalidOperationException : Node type None is not supported
>   at System.Xml.Linq.XNode.ReadFrom (System.Xml.XmlReader r, LoadOptions options)
Comment 1 Brendan Zagaeski (Xamarin Team, assistant) 2014-03-04 22:24:19 UTC
Created attachment 6222 [details]
Proposed fix

Note that even with this patch, the `Deserialize_System_Xml_Linq_XElement_AnyTypeArray` test will fail with the "Default constructor" error on Android and iOS platforms. The problem is that `System.Runtime.Serialization.XmlSerializableMap.DeserializeObject()` currently uses the version of `Activator.CreateInstance()` that only allows public constructors. This is likely because the `NET_2_1` constant is defined in these builds [1].

> [1] https://github.com/mono/mono/blob/62bf3c73c589ec994db7f4bcb9bae7f9da51b725/mcs/class/System.Runtime.Serialization/System.Runtime.Serialization/SerializationMap.cs#L417-L421
Comment 2 Atsushi Eno 2015-03-17 16:15:16 UTC
I have been reviewing xml related bug and now I'm trying to build the test attached on the first report post, but for System.Runtime.Serialization_test_net_4_5.dll it doesn't.

You need to add your test files to System.Runtime.Serialization_test.dll.sources. I added two files
- Test/System.Runtime.Serialization/DataContractSerializerTest_FrameworkTypes_System.Xml.Linq.cs
- Test/System.Runtime.Serialization/DataContractSerializerTest_FrameworkTypes.cs (which you seem to depend on. It was somehow not part of the test build.)

But I still get build error.

Test/System.Runtime.Serialization/DataContractSerializerTest_FrameworkTypes_System.Xml.Linq.cs(75,22): error CS0103: The name `Deserialize' does not exist in the current context
Test/System.Runtime.Serialization/DataContractSerializerTest_FrameworkTypes_System.Xml.Linq.cs(79,11): error CS1501: No overload for method `IsTrue' takes `4' arguments

Maybe you have some additional changes?
Comment 3 Brendan Zagaeski (Xamarin Team, assistant) 2016-05-05 00:06:58 UTC
Created attachment 15911 [details]
Test case

Whoops. Looking back on it, I'm not sure where I came up with that version of the `Deserialize()` method. I have attached a working test case as a plain console C# app.

## Verification status: verified fixed in Cycle 6 (Mono 4.2)

GOOD: Mono 4.4.0 (81f38a9)
GOOD: Mono 4.2.1 (6dd2d0d)
BAD:  Mono 4.0.5 (1d8d582)

I believe this was fixed by the import of the implementations from https://github.com/microsoft/referencesource in Mono 4.2.