如何确定哪个 class 成员导致 XmlSerializer 出现问题
How to identify which class member is causing issue with XmlSerializer
所以我在玩 XmlSerializer
,遇到了一些挑战。
我有一个第三方 XML 文件,所以我受到 XML 模式的限制,我不是它的忠实粉丝..我想尝试从这个 XML 到 VBA,所以我需要通过 COM。
我已经设法让一些 classes 工作,主要是使用 XSD2 (CodeDom) 将 XML 模式转换为一组 classes,并操纵C# classes 使用 IList<>
实现 classes。我认为问题出在哪里。我已经用几个 classes 作为开始,它工作得很好,我已经通过 CodeDom 推动它,现在有 335 classes/enumerations 领域的东西要处理。
我得到的异常在线
XmlSerializer serializer = new XmlSerializer(typeof(L5XSchema.RSLogix5000Content));
例外情况是:
'CDF15337, DDB176069: Also fail in whidbey IEnumerable member with XmlAttributeAttribute'
(link 是我能找到的异常的唯一在线参考 https://github.com/microsoft/referencesource/blob/master/System.Xml/System/Xml/Serialization/XmlSerializationWriterILGen.cs)
有谁知道 'whidbey' 参考资料是关于什么的?
幸运的是,由于 IEnumerable XmlAttributeAttribute 部分,我相信我已经确定了问题。
确实如此。将它们转换回 Enum[] 允许构建 XmlSerializer
实例。我的 XML 此部分(属性之一)的架构如下所示:
<xs:attribute name="CIPAxisExceptionAction" use="optional">
<xs:simpleType>
<xs:list itemType="AxisExceptionActionEnum" />
</xs:simpleType>
</xs:attribute>
有谁知道一种方法可以将 IList<>
之类的东西用于 XML 属性值?
.NET Framework 示例问题:https://dotnetfiddle.net/Ws5VMI
.NET Core 示例问题:https://dotnetfiddle.net/ShO5UX
我目前仍在努力根据架构获得 classes 'happy' 和 XmlSerializer
。并且对 COM 也很满意。这意味着没有可识别的通用集合,所以我的所有 List<>
成员都需要包装在一个 IList<>
实现包装器 class 中才能通过 COM 障碍。在处理派生的 classes 时非常烦人。
有谁知道可以轻松地将 C# 中的泛型集合代理到 COM 的方法吗?
您发现 XmlSerializer
的 code-generation 算法中似乎存在错误,特别是 自定义 collection 标记为 [=19] 的枚举=] 未实现 non-generic 接口 System.Collections.ICollection
。解决方法似乎是在自定义 collection.
上显式实现此接口
详情如下。假设我们有如下类型:
public class RSLogix5000Content
{
[XmlAttribute] public List<AxisExceptionActionEnum> CIPAxisExceptionAction { get; set; }
}
public enum AxisExceptionActionEnum
{
Default = 0,
Value1 = (1<<0),
Value2 = (1<<1)
}
当序列化为XML时,属性被成功序列化为包含space-separated值序列的字符串,如下所示(demo fiddle #1 here):
<RSLogix5000Content CIPAxisExceptionAction="Value1 Value2 Value1" />
但是,如果 List<AxisExceptionActionEnum>
被自定义 collection 替换,实现 IList<AxisExceptionActionEnum>
就像这样(演示 fiddle #2 here):
public class RSLogix5000Content
{
[XmlAttribute]
public AxisExceptionActionEnumCollection CIPAxisExceptionAction { get; set; }
}
public enum AxisExceptionActionEnum
{
Default = 0,
Value1 = (1<<0),
Value2 = (1<<1)
}
public class AxisExceptionActionEnumCollection : IList<AxisExceptionActionEnum>
{
private System.Collections.Generic.List<AxisExceptionActionEnum> axisExceptionActionEnumField = new System.Collections.Generic.List<AxisExceptionActionEnum>();
[System.Xml.Serialization.XmlIgnoreAttribute()]
public AxisExceptionActionEnum this[int index] {
get {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField))[index];
}
set {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField))[index] = value;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public int Count {
get {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Count;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool IsReadOnly {
get {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).IsReadOnly;
}
}
public void Add(AxisExceptionActionEnum item) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Add(item);
}
public void Clear() {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Clear();
}
public bool Contains(AxisExceptionActionEnum item) {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Contains(item);
}
public void CopyTo(AxisExceptionActionEnum[] array, int arrayIndex) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).CopyTo(array, arrayIndex);
}
public int IndexOf(AxisExceptionActionEnum item) {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).IndexOf(item);
}
public void Insert(int index, AxisExceptionActionEnum item) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Insert(index, item);
}
public bool Remove(AxisExceptionActionEnum item) {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Remove(item);
}
public void RemoveAt(int index) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).RemoveAt(index);
}
public System.Collections.Generic.IEnumerator<AxisExceptionActionEnum> GetEnumerator() {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).GetEnumerator();
}
}
然后抛出一个莫名其妙的异常:
System.NotSupportedException: Also fail in IEnumerable member with XmlAttributeAttribute
at System.Xml.Serialization.XmlSerializationWriterILGen.WriteMember(SourceInfo source, AttributeAccessor attribute, TypeDesc memberTypeDesc, String parent)
at System.Xml.Serialization.XmlSerializationWriterILGen.WriteStructMethod(StructMapping mapping)
at System.Xml.Serialization.XmlSerializationWriterILGen.GenerateMethod(TypeMapping mapping)
at System.Xml.Serialization.XmlSerializationILGen.GenerateReferencedMethods()
at System.Xml.Serialization.XmlSerializationWriterILGen.GenerateEnd()
at System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace)
at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location)
at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace, String location)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type)
为什么会发生这种情况?好吧,List<AxisExceptionActionEnum>
和 AxisExceptionActionEnumCollection
之间的一个区别是前者实现了旧的 non-generic 接口 IList
和 ICollection
,也许缺少 non-generic访问 collection 是否导致序列化程序出现问题?为了测试这个,我为你的 collection:
实现了 ICollection
public class AxisExceptionActionEnumCollection : IList<AxisExceptionActionEnum>, ICollection
{
// Remainder unchanged
#region ICollection Members
void ICollection.CopyTo(Array array, int index)
{
((System.Collections.IList)(this.axisExceptionActionEnumField)).CopyTo(array, index);
}
int ICollection.Count { get { return Count; } }
bool ICollection.IsSynchronized { get { return ((System.Collections.IList)(this.axisExceptionActionEnumField)).IsSynchronized; } }
object ICollection.SyncRoot { get { return ((System.Collections.IList)(this.axisExceptionActionEnumField)).SyncRoot; } }
#endregion
}
问题自行解决!演示 fiddle #3 here.
备注:
只需要实现 non-generic ICollection
接口 -- 而不是 non-generic IList
.
XmlSerializer
非常乐意序列化您的 generic-only collection 只要它标有 [XmlElement]
而不是[XmlAttribute]
:
public class RSLogix5000Content
{
[XmlElement]
public AxisExceptionActionEnumCollection CIPAxisExceptionAction { get; set; }
}
演示 fiddle #4 here.
因为 ICollection
甚至没有 Add(object value)
方法,这绝对感觉像是一个 XmlSerializer
错误。您可能想向 Microsoft 报告问题,例如here.
将 collection 个枚举序列化为属性会导致 space-separated 个枚举值序列,这让我感到有些惊讶; docs 中没有任何内容表明这应该有效。
但是由于 [Flags]
枚举也被序列化为 space-separated 枚举值序列,似乎尝试将标志枚举列表序列化为 XML 属性也失败了有一个难以理解的例外:
public class RSLogix5000Content
{
[XmlAttribute] public List<AxisExceptionActionEnum> CIPAxisExceptionAction { get; set; }
}
[Flags]
public enum AxisExceptionActionEnum
{
Default = 0,
Value1 = (1<<0),
Value2 = (1<<1)
}
Failed with unhandled exception:
System.InvalidOperationException: There was an error reflecting type 'RSLogix5000Content'.
---> System.InvalidOperationException: There was an error reflecting property 'CIPAxisExceptionAction'.
---> System.InvalidOperationException: There was an error reflecting type 'AxisExceptionActionEnum'.
---> System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.
at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
at System.String.Format(String format, Object arg0)
at System.SR.Format(String resourceFormat, Object p1)
at System.Xml.Serialization.XmlReflectionImporter.ImportEnumMapping(EnumModel model, String ns, Boolean repeats)
演示 fiddle #5 here.
所以我在玩 XmlSerializer
,遇到了一些挑战。
我有一个第三方 XML 文件,所以我受到 XML 模式的限制,我不是它的忠实粉丝..我想尝试从这个 XML 到 VBA,所以我需要通过 COM。
我已经设法让一些 classes 工作,主要是使用 XSD2 (CodeDom) 将 XML 模式转换为一组 classes,并操纵C# classes 使用 IList<>
实现 classes。我认为问题出在哪里。我已经用几个 classes 作为开始,它工作得很好,我已经通过 CodeDom 推动它,现在有 335 classes/enumerations 领域的东西要处理。
我得到的异常在线
XmlSerializer serializer = new XmlSerializer(typeof(L5XSchema.RSLogix5000Content));
例外情况是: 'CDF15337, DDB176069: Also fail in whidbey IEnumerable member with XmlAttributeAttribute' (link 是我能找到的异常的唯一在线参考 https://github.com/microsoft/referencesource/blob/master/System.Xml/System/Xml/Serialization/XmlSerializationWriterILGen.cs)
有谁知道 'whidbey' 参考资料是关于什么的?
幸运的是,由于 IEnumerable XmlAttributeAttribute 部分,我相信我已经确定了问题。
确实如此。将它们转换回 Enum[] 允许构建 XmlSerializer
实例。我的 XML 此部分(属性之一)的架构如下所示:
<xs:attribute name="CIPAxisExceptionAction" use="optional">
<xs:simpleType>
<xs:list itemType="AxisExceptionActionEnum" />
</xs:simpleType>
</xs:attribute>
有谁知道一种方法可以将 IList<>
之类的东西用于 XML 属性值?
.NET Framework 示例问题:https://dotnetfiddle.net/Ws5VMI
.NET Core 示例问题:https://dotnetfiddle.net/ShO5UX
我目前仍在努力根据架构获得 classes 'happy' 和 XmlSerializer
。并且对 COM 也很满意。这意味着没有可识别的通用集合,所以我的所有 List<>
成员都需要包装在一个 IList<>
实现包装器 class 中才能通过 COM 障碍。在处理派生的 classes 时非常烦人。
有谁知道可以轻松地将 C# 中的泛型集合代理到 COM 的方法吗?
您发现 XmlSerializer
的 code-generation 算法中似乎存在错误,特别是 自定义 collection 标记为 [=19] 的枚举=] 未实现 non-generic 接口 System.Collections.ICollection
。解决方法似乎是在自定义 collection.
详情如下。假设我们有如下类型:
public class RSLogix5000Content
{
[XmlAttribute] public List<AxisExceptionActionEnum> CIPAxisExceptionAction { get; set; }
}
public enum AxisExceptionActionEnum
{
Default = 0,
Value1 = (1<<0),
Value2 = (1<<1)
}
当序列化为XML时,属性被成功序列化为包含space-separated值序列的字符串,如下所示(demo fiddle #1 here):
<RSLogix5000Content CIPAxisExceptionAction="Value1 Value2 Value1" />
但是,如果 List<AxisExceptionActionEnum>
被自定义 collection 替换,实现 IList<AxisExceptionActionEnum>
就像这样(演示 fiddle #2 here):
public class RSLogix5000Content
{
[XmlAttribute]
public AxisExceptionActionEnumCollection CIPAxisExceptionAction { get; set; }
}
public enum AxisExceptionActionEnum
{
Default = 0,
Value1 = (1<<0),
Value2 = (1<<1)
}
public class AxisExceptionActionEnumCollection : IList<AxisExceptionActionEnum>
{
private System.Collections.Generic.List<AxisExceptionActionEnum> axisExceptionActionEnumField = new System.Collections.Generic.List<AxisExceptionActionEnum>();
[System.Xml.Serialization.XmlIgnoreAttribute()]
public AxisExceptionActionEnum this[int index] {
get {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField))[index];
}
set {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField))[index] = value;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public int Count {
get {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Count;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool IsReadOnly {
get {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).IsReadOnly;
}
}
public void Add(AxisExceptionActionEnum item) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Add(item);
}
public void Clear() {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Clear();
}
public bool Contains(AxisExceptionActionEnum item) {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Contains(item);
}
public void CopyTo(AxisExceptionActionEnum[] array, int arrayIndex) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).CopyTo(array, arrayIndex);
}
public int IndexOf(AxisExceptionActionEnum item) {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).IndexOf(item);
}
public void Insert(int index, AxisExceptionActionEnum item) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Insert(index, item);
}
public bool Remove(AxisExceptionActionEnum item) {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).Remove(item);
}
public void RemoveAt(int index) {
((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).RemoveAt(index);
}
public System.Collections.Generic.IEnumerator<AxisExceptionActionEnum> GetEnumerator() {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return ((System.Collections.Generic.IList<AxisExceptionActionEnum>)(this.axisExceptionActionEnumField)).GetEnumerator();
}
}
然后抛出一个莫名其妙的异常:
System.NotSupportedException: Also fail in IEnumerable member with XmlAttributeAttribute at System.Xml.Serialization.XmlSerializationWriterILGen.WriteMember(SourceInfo source, AttributeAccessor attribute, TypeDesc memberTypeDesc, String parent) at System.Xml.Serialization.XmlSerializationWriterILGen.WriteStructMethod(StructMapping mapping) at System.Xml.Serialization.XmlSerializationWriterILGen.GenerateMethod(TypeMapping mapping) at System.Xml.Serialization.XmlSerializationILGen.GenerateReferencedMethods() at System.Xml.Serialization.XmlSerializationWriterILGen.GenerateEnd() at System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace) at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location) at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace, String location) at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace) at System.Xml.Serialization.XmlSerializer..ctor(Type type)
为什么会发生这种情况?好吧,List<AxisExceptionActionEnum>
和 AxisExceptionActionEnumCollection
之间的一个区别是前者实现了旧的 non-generic 接口 IList
和 ICollection
,也许缺少 non-generic访问 collection 是否导致序列化程序出现问题?为了测试这个,我为你的 collection:
ICollection
public class AxisExceptionActionEnumCollection : IList<AxisExceptionActionEnum>, ICollection
{
// Remainder unchanged
#region ICollection Members
void ICollection.CopyTo(Array array, int index)
{
((System.Collections.IList)(this.axisExceptionActionEnumField)).CopyTo(array, index);
}
int ICollection.Count { get { return Count; } }
bool ICollection.IsSynchronized { get { return ((System.Collections.IList)(this.axisExceptionActionEnumField)).IsSynchronized; } }
object ICollection.SyncRoot { get { return ((System.Collections.IList)(this.axisExceptionActionEnumField)).SyncRoot; } }
#endregion
}
问题自行解决!演示 fiddle #3 here.
备注:
只需要实现 non-generic
ICollection
接口 -- 而不是 non-genericIList
.XmlSerializer
非常乐意序列化您的 generic-only collection 只要它标有[XmlElement]
而不是[XmlAttribute]
:public class RSLogix5000Content { [XmlElement] public AxisExceptionActionEnumCollection CIPAxisExceptionAction { get; set; } }
演示 fiddle #4 here.
因为
ICollection
甚至没有Add(object value)
方法,这绝对感觉像是一个XmlSerializer
错误。您可能想向 Microsoft 报告问题,例如here.将 collection 个枚举序列化为属性会导致 space-separated 个枚举值序列,这让我感到有些惊讶; docs 中没有任何内容表明这应该有效。
但是由于
[Flags]
枚举也被序列化为 space-separated 枚举值序列,似乎尝试将标志枚举列表序列化为 XML 属性也失败了有一个难以理解的例外:public class RSLogix5000Content { [XmlAttribute] public List<AxisExceptionActionEnum> CIPAxisExceptionAction { get; set; } } [Flags] public enum AxisExceptionActionEnum { Default = 0, Value1 = (1<<0), Value2 = (1<<1) }
Failed with unhandled exception: System.InvalidOperationException: There was an error reflecting type 'RSLogix5000Content'. ---> System.InvalidOperationException: There was an error reflecting property 'CIPAxisExceptionAction'. ---> System.InvalidOperationException: There was an error reflecting type 'AxisExceptionActionEnum'. ---> System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list. at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args) at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args) at System.String.Format(String format, Object arg0) at System.SR.Format(String resourceFormat, Object p1) at System.Xml.Serialization.XmlReflectionImporter.ImportEnumMapping(EnumModel model, String ns, Boolean repeats)
演示 fiddle #5 here.