Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

DataContractSerializer bug when serializing nullable structs in .net >= 8.0 #126873

Copy link
Copy link

Description

@antmjones
Issue body actions

Description

In .NET >= 8, When a DataContractSerializer is used to serialize a nullable custom value type (Nullable<T>) and the corresponding type T has not previously been seen by a DataContractSerializer, an exception is thrown:

System.Runtime.Serialization.SerializationException: An internal error has occurred. DataContract cache overflow.
   at System.Runtime.Serialization.DataContracts.DataContract.DataContractCriticalHelper.GetIdForInitialization(ClassDataContract classContract)
   at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.CreateObject(ClassDataContract classContract)
   at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.GenerateClassReader(ClassDataContract classContract)
   at System.Runtime.Serialization.DataContracts.ClassDataContract.get_XmlFormatReaderDelegate()
   at System.Runtime.Serialization.DataContracts.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
   at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)

This appears to be a bug introduce in the following commit:

adaebca#diff-343a32c8e8d13ca319b989de56100d9b829145aa2f7be2908f814e78cbe12b6eL378

Specifically, the commit changed line 378 of DataContract.cs from:

if (ContractMatches(classContract, s_dataContractCache[i]))

to

if (ContractMatches(classContract, s_dataContractCache.GetItem(id)))

Note the change from i to id as the argument to GetItem(), which appears to be inadvertent.

Reproduction Steps

The following code works as expected (no exception thrown) when targetting .NET 6, but fails with an exception in .NET 8/9/10:

using System.Runtime.Serialization;
using System.Text;
using System.Xml;

namespace DataContractSerializerTest {
    internal class Program {
        [DataContract]
        private struct TestStruct1 {
        }

        static void Main(string[] args) {
            // if this line is uncommented , the code compiles and runs without any exceptions.
            // If it is commented out, an exception is thrown.
            //SerializeAndDeserialize<TestStruct1>(default(TestStruct1));
            SerializeAndDeserialize<TestStruct1?>(default(TestStruct1));
        }

        private static T? SerializeAndDeserialize<T>(T? obj) {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));

            StringBuilder stringBuilder = new StringBuilder();

            using (XmlWriter writer = XmlWriter.Create(stringBuilder)) {
                serializer.WriteObject(writer, obj);
            }

            using (XmlReader reader = XmlReader.Create(new StringReader(stringBuilder.ToString()))) {
                return (T?)serializer.ReadObject(reader);
            }
        }
    }
}

Expected behavior

Nullable<T> where T is a value type marked with [DataContract] can be serialized successfully as it was in .NET < 8.

Actual behavior

An exception is thrown:

System.Runtime.Serialization.SerializationException: An internal error has occurred. DataContract cache overflow.
   at System.Runtime.Serialization.DataContracts.DataContract.DataContractCriticalHelper.GetIdForInitialization(ClassDataContract classContract)
   at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.CreateObject(ClassDataContract classContract)
   at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.GenerateClassReader(ClassDataContract classContract)
   at System.Runtime.Serialization.DataContracts.ClassDataContract.get_XmlFormatReaderDelegate()
   at System.Runtime.Serialization.DataContracts.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
   at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)

Regression?

Yes, appears to work correctly in .NET <8, and appears to have been introduced in the commit adaebca#diff-343a32c8e8d13ca319b989de56100d9b829145aa2f7be2908f814e78cbe12b6eL378

Known Workarounds

Use a DataContractSerializer to serialize the non-nullable struct T before using it to serialize Nullable<T>.

Configuration

.NET 10, Windows 11, x86.

Other information

No response

Reactions are currently unavailable

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

Morty Proxy This is a proxified and sanitized view of the page, visit original site.