首页 > 解决方案 > 如何使用 SGen 为映射到两级数组的 XML 模式生成序列化程序集?

问题描述

我正在使用需要 XmlSerializerFormat 合同的第三方服务;我想通过创建序列化程序集来加快启动速度,但 Sgen.exe 确实不喜欢 Xsd.exe 为其吐出嵌套数组的架构中的特定构造。

该模式包括嵌套两层深度的元素序列,如下所示:

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>

即:一个顶层Foo包含许多FooLists,而aFooList包含许多FooListItem

运行xsd /c Foo.xsd产生以下结果:

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;
        }
    }
}

也就是说,FooList由于某种原因,没有类 for 存在,而是只有一个嵌套的FooListItems 数组。

但是,当我使用 just 构建它并在生成的 DLL 上运行 Sgen.exe 时,sgen /keep obj\Debug\net461\Foo.dll会出现以下错误消息:

错误 CS0030:无法将类型“FooListItem[]”转换为“FooListItem”
错误 CS0029:无法将类型“FooListItem”隐式转换为“FooListItem[]”

(我使用的是 .NET 4.7 版本的 Xsd.exe 和 Sgen.exe,我只是针对 4.6.1 的兼容性。)

查看生成的代码,它被以下方法阻塞:

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);
}

因此,似乎 Xsd.exe 和 Sgen.exe 试图实现元素具有包含 X 项的显式“X 列表”子元素的模式,而无需为列表元素创建单独的类,而仅依赖于序列化的名称合成中间元素的属性;当列表元素本身可能重复时,这会中断。

有没有办法解决这个问题?像强制 Xsd.exe 为中间元素生成一个类?我想这可能是 Xsd.exe 和 Sgen.exe 中的一个实际错误,但这在合理的时间范围内不会真正帮助我。


如上所述,这是第三方服务;我完全无法控制架构,并且生成代码的手动编辑越少越好,因为我的实际文件有数万行长。

标签: c#xmlxsdxmlserializersgen

解决方案


错误在这一行

来自:[XmlArrayItem("Item", typeof(FooListItem), IsNullable=false)]

To :"[XmlArrayItem("Item", IsNullable = false)]

这是工作代码示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {

            XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
            namespaces.Add("xs", "http://www.w3.org/2001/XMLSchema");
            namespaces.Add("", "http://example.com");

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            XmlWriter writer = XmlWriter.Create(FILENAME, settings);
            XmlSerializer serializer = new XmlSerializer(typeof(Foo));

            Foo foo = new Foo()
            {
                List = new FooListItem[][] {
                    new FooListItem[] { 
                        new FooListItem() { Value = "abc"},
                        new FooListItem() { Value = "abd"},
                        new FooListItem() { Value = "abe"}
                    },
                    new FooListItem[] { 
                        new FooListItem() { Value = "bbc"},
                        new FooListItem() { Value = "bbd"},
                        new FooListItem() { Value = "bbe"}
                    },
                    new FooListItem[] { 
                        new FooListItem() { Value = "cbc"},
                        new FooListItem() { Value = "cbd"},
                        new FooListItem() { Value = "cbe"}
                    }
                }
            };

            serializer.Serialize(writer, foo, namespaces);



        }
    }
    [XmlType(Namespace = "http://example.com")]
    [XmlRoot(Namespace = "http://example.com", IsNullable = false)]
    public partial class Foo
    {

        private FooListItem[][] listField;

        [XmlArrayItem("Item", 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;
            }
        }
    }
}

推荐阅读