diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index 0c08561ba20ced..ba7ea685facfb8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -1576,9 +1576,7 @@ private static XmlSerializationCollectionFixupCallback GetCreateCollectionOfObje { if (structMapping.TypeDesc.Type != null && typeof(XmlSchemaObject).IsAssignableFrom(structMapping.TypeDesc.Type)) { - // https://github.com/dotnet/runtime/issues/1399: - // To Support Serializing XmlSchemaObject - throw new NotImplementedException(nameof(XmlSchemaObject)); + DecodeName = false; } object? o = ReflectionCreateObject(structMapping.TypeDesc.Type!)!; @@ -1812,10 +1810,24 @@ private static XmlSerializationCollectionFixupCallback GetCreateCollectionOfObje { MemberInfo[] memberInfos = o!.GetType().GetMember(member.Mapping.Name); MemberInfo memberInfo = memberInfos[0]; - object? collection = null; - SetCollectionObjectWithCollectionMember(ref collection, member.Collection, member.Mapping.TypeDesc!.Type!); - var setMemberValue = GetSetMemberValueDelegate(o, memberInfo.Name); - setMemberValue(o, collection); + + // For read-only collection properties (no setter), add items directly to the existing + // collection instance returned by the getter. This matches what the IL-based serializer does. + if (memberInfo is PropertyInfo pi && pi.GetSetMethod(nonPublic: true) == null) + { + object? existingCollection = pi.GetValue(o); + if (existingCollection != null) + { + AddObjectsIntoTargetCollection(existingCollection, member.Collection, member.Mapping.TypeDesc!.Type!); + } + } + else + { + object? collection = null; + SetCollectionObjectWithCollectionMember(ref collection, member.Collection, member.Mapping.TypeDesc!.Type!); + var setMemberValue = GetSetMemberValueDelegate(o, memberInfo.Name); + setMemberValue(o, collection); + } } member.EnsureCollection?.Invoke(o!); diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs index 733b5ae83c1952..1178edc4606fc1 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.Internal.cs @@ -76,39 +76,4 @@ public static void XML_TypeWithFieldsOrdered() Assert.Equal(value.StringField1, actual.StringField1); Assert.Equal(value.StringField2, actual.StringField2); } - - // TODO - Move this test to XmlSerializerTests.RuntimeOnly.cs once #1399 is fixed for the ReflectionOnly serializer. - [Fact] - public static void Xml_XmlSchema() - { - var expectedXml = WithXmlHeader("\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n"); - - XmlSchema schema = new XmlSchema - { - TargetNamespace = "http://example.com/my-schema", - ElementFormDefault = XmlSchemaForm.Qualified - }; - schema.Items.Add(new XmlSchemaElement - { - Name = "MyElement", - SchemaTypeName = new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema") - }); - schema.Items.Add(new XmlSchemaGroup - { - Name = "MyGroup", - Particle = new XmlSchemaSequence - { - Items = { new XmlSchemaElement { Name = "Item1" }, new XmlSchemaElement { Name = "Item2" } } - } - }); - schema.Namespaces.Add("xsd", "http://www.w3.org/2001/XMLSchema"); - schema.Namespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance"); - - var actual = SerializeAndDeserialize(schema, expectedXml, () => new XmlSerializer(typeof(XmlSchema))); - - Assert.Equal(schema.TargetNamespace, actual.TargetNamespace); - Assert.Equal(schema.ElementFormDefault, actual.ElementFormDefault); - Assert.Equal(schema.Items.Count, actual.Items.Count); - } - } diff --git a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs index 07f0e6816fb2d0..441f35bf87a7fb 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSerializer/XmlSerializerTests.RuntimeOnly.cs @@ -861,6 +861,81 @@ public static void XML_XmlSchemaWithNamespacesWriteWithNamespaceManager() schema.Write(memStream, new XmlNamespaceManager(new NameTable())); } + [Fact] + public static void Xml_XmlSchema() + { + var expectedXml = WithXmlHeader("\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n"); + + XmlSchema schema = new XmlSchema + { + TargetNamespace = "http://example.com/my-schema", + ElementFormDefault = XmlSchemaForm.Qualified + }; + schema.Items.Add(new XmlSchemaElement + { + Name = "MyElement", + SchemaTypeName = new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema") + }); + schema.Items.Add(new XmlSchemaGroup + { + Name = "MyGroup", + Particle = new XmlSchemaSequence + { + Items = { new XmlSchemaElement { Name = "Item1" }, new XmlSchemaElement { Name = "Item2" } } + } + }); + schema.Namespaces.Add("xsd", "http://www.w3.org/2001/XMLSchema"); + schema.Namespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance"); + + var actual = SerializeAndDeserialize(schema, expectedXml, () => new XmlSerializer(typeof(XmlSchema))); + + Assert.Equal(schema.TargetNamespace, actual.TargetNamespace); + Assert.Equal(schema.ElementFormDefault, actual.ElementFormDefault); + Assert.Equal(schema.Items.Count, actual.Items.Count); + } + + [Fact] + public static void XmlSchemaObject_RoundTrip() + { + // Verify that XmlSchemaObject-derived types can be deserialized with the reflection-based + // serializer. Previously a NotImplementedException was thrown during deserialization. + var element = new XmlSchemaElement + { + Name = "TestElement", + SchemaTypeName = new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema") + }; + var serializer = new XmlSerializer(typeof(XmlSchemaElement)); + using var ms = new MemoryStream(); + serializer.Serialize(ms, element); + ms.Position = 0; + + var result = (XmlSchemaElement?)serializer.Deserialize(ms); + Assert.NotNull(result); + Assert.Equal("TestElement", result.Name); + Assert.Equal(new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema"), result.SchemaTypeName); + } + + [Fact] + public static void XmlSchemaObject_RoundTrip_WithConstraintInReadOnlyCollection() + { + // Verify that items in read-only collection properties (like XmlSchemaElement.Constraints) + // are correctly round-tripped using the reflection-based serializer. + var element = new XmlSchemaElement { Name = "TestElement" }; + element.Constraints.Add(new XmlSchemaKey { Name = "PrimaryKey" }); + + var serializer = new XmlSerializer(typeof(XmlSchemaElement)); + using var ms = new MemoryStream(); + serializer.Serialize(ms, element); + ms.Position = 0; + + var result = (XmlSchemaElement?)serializer.Deserialize(ms); + Assert.NotNull(result); + Assert.Equal("TestElement", result.Name); + Assert.Equal(1, result.Constraints.Count); + var key = Assert.IsType(result.Constraints[0]); + Assert.Equal("PrimaryKey", key.Name); + } + [Fact] public static void Xml_XmlElementAsRoot() {