Skip to content

Conversation

daeghanelkin
Copy link

Value types implementing the IXmlSerializable interface were not being property boxed when they declared a parameterless constructor. This caused the XmlSerializer to fail to deserialize them with an InvalidProgramException exception when the generated IL tried to call the XmlSerializationReader.ReadSerializable method with the value.

Fixes #99613


I would like to note that I am not really that familiar with IL generation, so all corrections are welcome ☺️.

I took the C# output of the serializer produced by the Microsoft.XmlSerializer.Generator tool for a different types and simplified it down to determine what the IL should look like for the call to the XmlSerializationReader.ReadSerializable method.

My simplified snippet was the following:

public class Class {
    public void Method() {
        Read(new StructWithParameterlessConstructor());

        Read(new StructWithParameterlessConstructor(), true);
        
        Read(new StructWithoutParameterlessConstructor());

        Read(new StructWithoutParameterlessConstructor(), true);
    }
    
    private static void Read(IInterface @interface) { }
    
    private static void Read(IInterface @interface, bool b) { }
}

public interface IInterface;

public struct StructWithParameterlessConstructor : IInterface
{
    public StructWithParameterlessConstructor() { }
}

public struct StructWithoutParameterlessConstructor : IInterface;

The following is the resulting IL for each method:

C# IL
Read(new StructWithParameterlessConstructor())
newobj instance void StructWithParameterlessConstructor::.ctor()
box StructWithParameterlessConstructor
call void Class::Read(class IInterface)
Read(new StructWithParameterlessConstructor(), true)
newobj instance void StructWithParameterlessConstructor::.ctor()
box StructWithParameterlessConstructor
ldc.i4.1
call void Class::Read(class IInterface)
Read(new StructWithoutParameterlessConstructor())
ldloca.s 0
initobj StructWithoutParameterlessConstructor
ldloc.0
box StructWithoutParameterlessConstructor
call void Class::Read(class IInterface)
Read(new StructWithoutParameterlessConstructor(), true)
ldloca.s 0
initobj StructWithoutParameterlessConstructor
ldloc.0
box StructWithoutParameterlessConstructor
ldc.i4.1
call void Class::Read(class IInterface)

No boxing was being written by the IL generator for the value types with parameterless constructors when the XML serializer was generating the runtime assembly, so a simple addition of that boxing seems to resolve the issue.
Value types without parameterless constructors do not run into this issue because they fall into the sm.TypeDesc.CannotNew condition above the new condition I've added, and avoid the style of instantiation that's demonstrated in my simplified example.

@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jul 9, 2025
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jul 9, 2025
@daeghanelkin daeghanelkin force-pushed the fix/xml-serialization-of-structs-with-parameterless-constructors branch from b03bd96 to 9ab4028 Compare July 9, 2025 21:24
@filipnavara filipnavara added area-System.Xml and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jul 10, 2025
@daeghanelkin daeghanelkin force-pushed the fix/xml-serialization-of-structs-with-parameterless-constructors branch from 9ab4028 to 33799e7 Compare July 10, 2025 15:32
@daeghanelkin
Copy link
Author

@dotnet-policy-service agree

@daeghanelkin daeghanelkin marked this pull request as ready for review July 10, 2025 15:40
@Copilot Copilot AI review requested due to automatic review settings July 10, 2025 15:40
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a missing boxing step for value types implementing IXmlSerializable during IL generation in XmlSerializationReader, preventing runtime errors when deserializing such structs. It also adds test types and unit tests to verify correct behavior for structs with and without parameterless constructors.

  • Added a new branch in the IL generator to convert (box) value types to IXmlSerializable.
  • Introduced two struct types in tests to exercise both cases (with and without parameterless constructor).
  • Added corresponding unit tests to validate serialization and deserialization of those structs.

Reviewed Changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTypes.cs Added two IXmlSerializable structs (with and without parameterless ctor)
src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.cs Added tests for serializing/deserializing the new structs
src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs Updated IL gen to box value types implementing IXmlSerializable

@daeghanelkin
Copy link
Author

@filipnavara Thanks for the explicit label! I also notice that there's an entry called area-Serialization which also includes System.Xml.XmlSerializer, should that label also be added here or is that a more legacy label than the one you've already attached?

@filipnavara
Copy link
Member

filipnavara commented Jul 15, 2025

area-Serialization recently changed ownership, but it can be more appropriate. cc @StephenMolloy

…nerated IL

Value types implementing the `IXmlSerializable` interface were not being
property boxed when they declared a parameterless constructor. This
caused the `XmlSerializer` to fail to deserialize them with an
`InvalidProgramException` exception when the generated IL tried to call
the `XmlSerializationReader.ReadSerializable` method with the value.

Fixes dotnet#99613
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Serialization community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IXmlSerializable on a struct with a parameterless constructor throws an InvalidProgramException on Derserialize
3 participants