I'm consuming a third-party service that requires XmlSerializerFormat contracts; I want to speed up startup by creating a serialization assembly, but Sgen.exe really doesn't like a particular construct in the schema that Xsd.exe spits out a nested array for.
The schema includes sequences of elements nested two levels deep like so:
Foo.xsd
<xs:schema targetNamespace="http://example.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://example.com" elementFormDefault="qualified">
<xs:element name="Foo" type="Foo"/>
<xs:complexType name="Foo">
<xs:sequence>
<xs:element name="List" type="FooList" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FooList">
<xs:sequence>
<xs:element name="Item" type="FooListItem" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="FooListItem">
<xs:simpleContent>
<xs:extension base="xs:string"/>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
That is: a toplevel Foo
contains many FooList
s, and a FooList
contains many FooListItem
.
Running xsd /c Foo.xsd
produces the following:
Foo.cs
using System.Xml.Serialization;
[XmlType(Namespace="http://example.com")]
[XmlRoot(Namespace="http://example.com", IsNullable=false)]
public partial class Foo {
private FooListItem[][] listField;
[XmlArrayItem("Item", typeof(FooListItem), IsNullable=false)]
public FooListItem[][] List {
get {
return this.listField;
}
set {
this.listField = value;
}
}
}
[XmlType(Namespace="http://example.com")]
public partial class FooListItem {
private string valueField;
[XmlText]
public string Value {
get {
return this.valueField;
}
set {
this.valueField = value;
}
}
}
That is, no class for FooList
is present for some reason, instead there's just a nested array of FooListItem
s.
However, when I build this and run Sgen.exe on the resulting DLL using just sgen /keep obj\Debug\net461\Foo.dll
, this chokes on the following error messages:
error CS0030: Cannot convert type 'FooListItem[]' to 'FooListItem'
error CS0029: Cannot implicitly convert type 'FooListItem' to 'FooListItem[]'
(I'm using .NET 4.7 versions of Xsd.exe and Sgen.exe, I'm just targeting 4.6.1 for compatibility.)
Looking at the generated code, it chokes in the following method:
void Write3_Foo(string n, string ns, global::Foo o, bool isNullable, bool needType) {
if ((object)o == null) {
if (isNullable) WriteNullTagLiteral(n, ns);
return;
}
if (!needType) {
System.Type t = o.GetType();
if (t == typeof(global::Foo)) {
}
else {
throw CreateUnknownTypeException(o);
}
}
WriteStartElement(n, ns, o, false, null);
if (needType) WriteXsiType(@"Foo", @"http://example.com");
{
// THIS SEEMS TO BE THE ROOT CAUSE
global::FooListItem[][] a = (global::FooListItem[][])((global::FooListItem[][])o.@List);
if (a != null){
WriteStartElement(@"List", @"http://example.com", null, false);
for (int ia = 0; ia < a.Length; ia++) {
// ERROR IS REPORTED HERE
Write2_FooListItem(@"Item", @"http://example.com", ((global::FooListItem)a[ia]), false, false);
}
WriteEndElement();
}
}
WriteEndElement(o);
}
So it seems like Xsd.exe and Sgen.exe try to realize the pattern where an element has an explicit "list of X" child containing X items without creating a separate class for the list element, but only relying on the name of the serialized property to synthetise the intermediate element; and this breaks when the list element itself may be repeated.
Is there a way to work around this? Like force Xsd.exe to generate a class for the intermediate element? I suppose this might be an actual bug in Xsd.exe and Sgen.exe, but that won't really help me in a reasonable time frame.
As said above, this is a third-party service; I have absolutely no control over the schema and the less manual editing of generated code involved the better since my actual files are tens of thousands of lines long.