如何使用 SGen 为映射到二级数组的 XML 架构生成序列化程序集?
How to use SGen to generate a serialization assembly for a XML schema that is mapped to a two-level array?
我正在使用需要 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
包含多个FooList
,一个FooList
包含多个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
不存在 class,而只有 FooListItem
的嵌套数组。
但是,当我仅使用 sgen /keep obj\Debug\net461\Foo.dll
在生成的 DLL 上构建它和 运行 Sgen.exe 时,会出现以下错误消息:
error CS0030: Cannot convert type 'FooListItem[]' to 'FooListItem'
error CS0029: Cannot implicitly convert type 'FooListItem' to 'FooListItem[]'
(我使用的是 Xsd.exe 和 Sgen.exe 的 .NET 4.7 版本,我只是为了兼容 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 试图实现这样一种模式,其中一个元素有一个明确的 "list of X" 包含 X 项的子元素,而不需要为它创建一个单独的 class列表元素,但仅依靠序列化的名称 属性 来合成中间元素;当列表元素本身可能重复时,这会中断。
有办法解决这个问题吗? 喜欢强制 Xsd.exe 为中间元素生成 class 吗?我想这可能是 Xsd.exe 和 Sgen.exe 中的一个实际错误,但这在合理的时间范围内不会真正帮助我。
如上所述,这是第三方服务;我完全无法控制架构,并且生成代码的手动编辑越少越好,因为我的实际文件有数万行长。
错误在这一行
发件人:[XmlArrayItem("Item", typeof(FooListItem), IsNullable=false)]
至:[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;
}
}
}
}
我正在使用需要 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
包含多个FooList
,一个FooList
包含多个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
不存在 class,而只有 FooListItem
的嵌套数组。
但是,当我仅使用 sgen /keep obj\Debug\net461\Foo.dll
在生成的 DLL 上构建它和 运行 Sgen.exe 时,会出现以下错误消息:
error CS0030: Cannot convert type 'FooListItem[]' to 'FooListItem'
error CS0029: Cannot implicitly convert type 'FooListItem' to 'FooListItem[]'
(我使用的是 Xsd.exe 和 Sgen.exe 的 .NET 4.7 版本,我只是为了兼容 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 试图实现这样一种模式,其中一个元素有一个明确的 "list of X" 包含 X 项的子元素,而不需要为它创建一个单独的 class列表元素,但仅依靠序列化的名称 属性 来合成中间元素;当列表元素本身可能重复时,这会中断。
有办法解决这个问题吗? 喜欢强制 Xsd.exe 为中间元素生成 class 吗?我想这可能是 Xsd.exe 和 Sgen.exe 中的一个实际错误,但这在合理的时间范围内不会真正帮助我。
如上所述,这是第三方服务;我完全无法控制架构,并且生成代码的手动编辑越少越好,因为我的实际文件有数万行长。
错误在这一行
发件人:[XmlArrayItem("Item", typeof(FooListItem), IsNullable=false)]
至:[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;
}
}
}
}