序列化List <t>的XmlSerializer的构造函数在与XmlAttributeOverrides

时间:2017-11-05 09:23:25

标签: c# xml list serialization

摘要

使用XmlSerializer课程时,使用List<T>序列化XmlSerializer(其中T可以XmlAttributeOverrides无序地序列化),如下所示:

using xmls = System.Xml.Serialization;
...
            xmls.XmlAttributeOverrides attributeOverrides = new xmls.XmlAttributeOverrides();
            attributeOverrides.Add(typeof(T), new xmls.XmlAttributes()
            {
                XmlRoot = new xmls.XmlRootAttribute("foo")
            });
            attributeOverrides.Add(typeof(List<T>), new xmls.XmlAttributes()
            {                    
                XmlArray = new xmls.XmlArrayAttribute("foobar"),
                XmlArrayItems = { new xmls.XmlArrayItemAttribute("foo") },                    
            });

将在最内层的异常处抛出以下InvalidOperationExcpetion:

System.InvalidOperationException: XmlRoot and XmlType attributes may not be specified for the type System.Collections.Generic.List`1[[T, programname, Version=versionnumber, Culture=neutral, PublicKeyToken=null]].

我对序列化程序的期望

<texparams>
    <texparam pname="TextureMinFilter" value="9729"/>
    <texparam pname="TextureMagFilter" value="9729"/>
</texparams>

目前我能成功获得

<ArrayOfTextureParameter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <TextureParameter pname="TextureMinFilter" value="9729" />
    <TextureParameter pname="TextureMagFilter" value="9728" />
</ArrayOfTextureParameter>

背景资讯

我最近一直在搞乱XML序列化,但遇到了一个问题。我正在尝试序列化和反序列化一些包装OpenGL纹理的类。

在我的一个类(我恰当地称为BitmapTexture2d)中,我有一个Bitmap字段,我希望将其存储在Base64元素中,如下所示:

<bitmap64>
    *base64goeshere*
</bitmap64>

由于我希望尽可能保持代码整洁,我决定使用IXmlSerializable界面而不是创建可以来回转换stringBitmap的属性。

在此过程的后期,我决定使用XmlSerializer类为Texture2dBitmapTexture2d派生的)Parameters中定义的单个字段生成XML。 (List<TextureParameter>TextureParameter可由XmlSerialization类序列化。然而,这就是序列化程序默认序列化List<TextureParameter>

的方式
<ArrayOfTextureParameter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <TextureParameter pname="TextureMinFilter" value="9729" />
    <TextureParameter pname="TextureMagFilter" value="9728" />
  </ArrayOfTextureParameter>

看到这个之后,我决定尝试更改节点的名称。经过一些研究(我多次登陆stackoverflow),我发现XmlAttributeOverrides类可以传递给XmlSerializer的构造函数来添加/覆盖节点名称等。

在写出我的代码之后,子序列化器的构造函数开始抛出异常,如上所述。我尝试使用一个抛出相同异常的数组。我后来虽然逐一序列化列表中的每个元素,但得出的结论是,它比我想象的那样难以实现。我在其他地方发布了这个问题没有答案。我在这里......

1 个答案:

答案 0 :(得分:3)

您的问题是,您尝试使用替换将[XmlArray][XmlArrayItem]附加到类型List<T> 。但是,如docs所示,[XmlArray]不能以这种方式使用:

[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field 
                        | AttributeTargets.Parameter | AttributeTargets.ReturnValue, 
    AllowMultiple = false)]
public class XmlArrayAttribute : Attribute

请注意,不包含AttributeTargets.Class?这意味着[XmlArray] 无法直接应用于类型,因此尝试通过XML覆盖正确地执行此操作会引发异常。

但至于为什么该异常消息表明,

  

System.InvalidOperationException:可能没有为System.Collections.Generic.List`1 ...

类型指定 XmlRoot XmlType 属性
呃,嗯,那个消息是完全错误的。它似乎是XmlSerializer中的一个小错误。如果需要,您甚至可以向Microsoft报告。

您需要做的是:

  • 覆盖List<T>的{​​{3}}属性以指定所需的名称,在本例中为“texparams”,AND

  • 覆盖T的{​​{3}}属性,并将[XmlRoot]设置为所需的集合元素名称。如果没有[XmlArrayItem(name)]覆盖,则控制项目类型为T的集合的元素名称。

因此,您的代码应如下所示:

static XmlSerializer MakeListSerializer<T>(string rootName, string elementName)
{
    xmls.XmlAttributeOverrides attributeOverrides = new xmls.XmlAttributeOverrides();
    attributeOverrides.Add(typeof(List<T>), new xmls.XmlAttributes()
    {
        XmlRoot = new xmls.XmlRootAttribute(rootName),
    });
    attributeOverrides.Add(typeof(T), new xmls.XmlAttributes()
    {
        XmlType = new xmls.XmlTypeAttribute(elementName),
    });

    return new XmlSerializer(typeof(List<T>), attributeOverrides);
}

示例[XmlType]

请注意,使用XmlSerializer构建XmlAttributeOverrides时,必须缓存序列化程序以便以后重用,以避免严重的内存泄漏,原因如XmlTypeAttribute.TypeName所述。